Skip to main content

Full text of "Programming Your Own Adventure Games In Pascal by Richard C. Vile, Jr."

See other formats


1768 | | | : ee 


| PROGRAMMING YOUR OWN | 
DVENTURE GAMES IN 


Adventures are played with Caps Lock off 
You can break out of programs with Control-C or Control-Shift-@ 
Notes in red added by Michael Bean, September 2022 


A note about disk Drive numbers in file names and commands: 


Pascal refers to Drive 1 and 2 as Volumes #4: and #5:. (The number 
sign and colons are usually typed as part of the drive name, but 
not always.) If no volume number is specified, Pascal defaults to 
using volume #4: Pascal can also be directed to use a specific disk, 
regardless of which drive it’s in, by typing the name of the disk 
instead of the drive volume (such as APPLEO: Again, the colon is 
typically typed as part of the disk name.) 


As written, the programs and commands in this book do not 
specify a drive volume, and Pascal expects any needed files to be 
on the disk which is in Drive 1. When entering the programs or 
commands, it will often be more convenient to tell Pascal to look 
on Drive 2 (Volume #5:) instead. 

Examples: 

‘#5:a2.db80.x’ 

(*$i#5:mini1 .text*) 

{Su#5:mt1.code} 

Edit what file? #5:mtadvent 


Alternatively you can specify the name of the disk on which a file is 
stored. This way no matter which drive the disk is in, Pascal will find 
the files when it needs them, as long as the specified disk is in one 
of the drives. However if the disk name is ever changed, or if the 
file is copied to a different disk, Pascal won't be able to find the 
files. 

Examples: 

‘diskname:a2.db80.x’ 

(*Sidiskname:mini1 .text*) 

{Sudiskname:mt1.code} 

Edit what file? diskname:mtadvent 


Pages 25, 81, 203, 207, 209, 224, 225, 233, 234, 261, 262, 263, 273, 
274, 282 and 283 utilize the “include” or “uses” instructions. 
Specifying a disk name or drive volume here is only important 
during the Compiling and Linking process, and does not affect the 
final game. 


Pages 83 and 232 refer to description database files used by 
Adventure 2 and 3. Whatever volume number or disk name is used 
here is hard-coded into the game, and Pascal will not look 
elsewhere. It might be best to leave these as written, and ensure 
that all Adventure files will be in Drive 1 when played. 


PROGRAMMING YOUR OWN 
ADVENTURE GAMES 
IN PASCAL 


PROGRAMMING YOUR OWN 
ADVENTURE GAMES 
IN PASCAL 


BY RICHARD C. VILE, JR. 


[TaB) TAS BOOKS Inc. 
BLUE RIDGE SUMMIT, PA. 17214 


FIRST EDITION 
FIRST PRINTING 


Copyright© 1984 by TAB BOOKS Inc. 
Printed in the United States of America 


Reproduction or publication of the content in any manner, without express 
permission of the publisher, is prohibited. No liability is assumed with respect to 
the use of the information herein. 


Library of Congress Cataloging in Publication Data 


Vile, Richard C., 1943- 
Programming your own adventure games in Pascal 
Includes index. 
1. Computer games. 2. PASCAL (Computer program 
language) |. Title. 
GV1469.2.V55 1984 794.8'2 84-8577 
ISBN 0-8306-0768-4 
ISBN 0-8306-1768-X (pbk.) 


Cover illustration by Larry Selman 


Contents 


Introduction 


The Elements of Adventure Games 


Rooms or Locations—Objects and Treasures—Beings and Monsters—Problems 


Notations for Describing Adventures 
Rooms or Locations—Travel Indicators— Problems—Objects— Beings and Monsters—Embellishing the 
Map—Map Layout and Organization—Examples 


Adventures and Problem Solving 
Clues to Problem Solutions—Problem Difficulty: From the Obvious to the Obscure—Repeatability— 
Logic— Surprise 


UCSD Pascal Review 
What You Should Know about Pascal—What Is the UCSD System? 


Preview of Adventure 1 


Preview Chapters—Preview of Adventure 1—Chapter Previews 


Pascal Adventure 1 


Listing 6-1. Pascal Miniadventure 


Representing the Map 


Representing the Adventure Map—Enumerated Types in Pascal 


viii 


12 


15 


23 


54 


15 


16 


17 


18 


1g 


20 


21 


22 


23 


Controlling the Play 


Case Statements in Pascal—Controlling the Adventure Game 


Mazes in the Middle 


Local Declarations—Local Procedures and Functions 


Other Techniques Used in Adventure 1 


Preview of Adventure 2 


Maps, Diagrams, and Code Outlines—Chapter Previews 


Pascal Adventure 2 


Command Processing in Adventure 2 


An Overview of the Command Handling Code—Command Processing in Detail 


Carry and Drop: Pascal Sets 
What Are Sets and How Can They Be Used? 


Problems in Adventure 2 


Events—Boolean Expressions and Events—Problems in Adventure 2 


Other Techniques Used in Adventure 2 


Counting Turns—Displaying the Contents of a Set—Simplification of Travel—The Use of File 
Variables—Scoring Your Adventures— The Help Command—The Lamp and the Light Command—The 


Dig Command—The Eat Command—The Ogre 


UCSD Pascal Development Techniques 
Managing Your Files Effectively—UCSD System Tricks and Pitfalls 


Preview of MAKEDESC and BROWSE 


Database Concepts—Previews of Chapters 19 through 24 


MAKEDESC and BROWSE 


Listing 19-1. Make80 Database Generator—Listing 19-2. Browse80 Database Snooper 


Creating a Database of Descriptions 


MAKEDESC: The Descriptions Generator—How to Use MAKEDESC Instruction Lines—Ditto Lines— 


Continuation File Lines— Running MAKEDESC 


Random Access Files in UCSD Pascal 


What Are Random and Sequential Files?—The File Creating Program—The File Reading Program— 
Random Access—The BROWSE Program—Using Descriptions Databases in Your Adventures 


The Structure of Adventure Databases 


Index Files and the Descriptions Index—Using the Descriptions Databases in Adventure Games— The 


BROWSE Program for Previewing Databases 


Programming Techniques Used in MAKEDESC 


Symbol Tables—Lookup Techniques: The Linear Search—Hashing: A More Efficient Search 


Technique —The Hash Table Lookup Technique 


59 


63 


66 


68 


78 


115 


125 


130 


135 


139 


145 


147 


166 


173 


177 


182 


24 


25 


26 


27 


28 


29 


30 


What Else Can You Put on Disk? 


Other Descriptions—Putting the Whole Program on Disk: Adventure Interpreters 


Preview of Adventure 3 


Outlines, Diagrams, and Maps—Chapter Previews 


The Listings of Adventure 3 

Listing 26-1. Adventure 3 Main Program—Listing 26-2. Location Procedure Unit—Listing 26-3. Com- 
mands 1 Unit—Listing 26-4. Commands 2 Unit—Listing 26-5. Commands 3 Unit—Listing 26-6. Problems 
Unit—Listing 26-7. Adventure 3 Data 


Larger Programs: Using UCSD Units 
Why Units?—The Units in Adventure 3—Units: Syntax and Semantics—Linking Programs that Have 
Units—Maintaining a Program Written with Units 


Problems in Adventure 3 
The Probs Unit—The Problems in Adventure 3—Implementing Adventure 3 Problems 


Other Techniques Used in Adventure 3 
Putting More into the Database—Coding Techniques to Get Around UCSD Limitations—Trickery in 
Showgoodies 


Writing Your Own Adventures 
Extending Adventure 3—Using a Skeleton Adventure—A Systematic Approach to Writing Adventures— 
Farewell 


Appendix A Skeleton Adventure Program 
Appendix B_- The Adventure 2 Database 
Appendix C The Adventure 3 Database 


Index 


190 


192 


203 


239 


245 


250 


254 


261 


286 


292 


304 


Introduction 


Welcome to the world of adventure games and Pas- 
cal. The purpose of this book is to teach you how to 
use the Pascal programming language to create 
adventure games on your home computer. I shall 
cover topics related both to the design and creation 
of the games themselves and to the style and use of 
the Pascal language. I shall concentrate on the style 
of adventure game referred to as a text adventure. I 
shall use the UCSD implementation of Pascal, 
which is widely available on microcomputers. 


ADVENTURE GAMES 

Adventure originated in the 1970s. The first 
adventure was a game written by Don Woods and 
Willie Crowther. The language used was FOR- 
TRAN and the computer used was a PDP-10, a 
mainframe computer common in universities and 
research laboratories. The game was an exercise in 
problem solving, artificial intelligence, and simula- 
tion. The main goal of the original adventure was to 
be a problem-solving exercise. 

The original adventure found its way onto 
many different computer installations. Since it was 


viii 


written in FORTRAN, people moved it to other 
computers besides the PDP-10. It was rewritten in 
other FORTRAN dialects and in other languages as 
well. In the early 1980s, it first became available for 
microcomputers. Microsoft, Inc. as well as other 
software companies adapted the game for use on 
personal microcomputers such as the Apple II and 
the TRS-80. 

Since the advent of adventure on microcom- 
puters, an entire subindustry of adventure game 
software producers has been created. Many com- 
panies created and sold adventures in the spirit of 
the original adventure. Most notable of these is 
probably the series of games known as Zork. Zork 
was also originally written on a large computer. 
The authors, several M.I.T. students, created a 
company called Infocom and began marketing Zork 
and its descendants. 

A programmer from Stromberg-Carlson in 
Florida, Scott Adams, created a system for writing 
adventure games on the TRS-80. He published an 
article in Creative Computing magazine describing 
the system and went on to create and market sev- 


eral of his own adventure games. These games and 
Adams’ company, Adventure International have be- 
come industry standards. 

A company in California, now known as Sierra 
On-Line Systems, created several unusual adven- 
tures for the Apple II. What made these adventures 
unusual was that they used the Apple high- 
resolution graphics display to show the player a 
picture of each location in the adventure as it was 
reached. Thus, textual descriptions and creative 
prose was replaced by graphics artistry. Several 
companies have now started writing and marketing 
adventure games in this style. Such adventures are 
referred to as hi-res adventures in honor of the 
original implementation. 

Several adventure games that mimic the style 
and approach of the noncomputerized Dungeons and 
Dragons fantasy role-playing games are avail- 
able. Dungeons and Dragons was admittedly the 
inspiration for the original adventure. However, 
the style of original adventure was unlike that of D 
and D. In the D and D style games characters are 
created or “rolled.” (The terminology is derived 
from the fact that several different kinds of dice are 
used in the character creation process.) The 
characters have attributes such as strength, intelli- 
gence, wisdom, and charisma, with numerical val- 
ues attached. These attributes play an integral part 
in the progress of the game. 

In this book I will deal strictly with text-style 
adventures in the spirit of original adventure. The 
emphasis will be on the creation and solving of 
interesting problems. The nature of adventure 
game problems is discussed in detail. The methods 
of representing and handling problems using Pascal 
programming techniques are given extensive 
coverage in the text. It is my belief that the scope 
for creativity in text adventures is great. There are 
as many adventures out there waiting to be in- 
vented as there are adventure and fantasy novels 
waiting to be written. The range of possibilities is 
limited only by your imagination and your pro- 
gramming skills. 


PASCAL 
After BASIC, Pascal is the most popular pro- 


gramming language available for personal and mi- 
crocomputers. It has been called by some the 
“language of the 80s.” Since its creation in the late 
1960s, it has spread throughout the world, growing 
in popularity and acceptance each year. When a 
committee was formed in late 1979 to investigate 
the standardization of the language, it attracted the 
attention of many of the leading companies in the 
computer and electronics fields. The interest in 
obtaining a language standard was almost unpre- 
cedented in the computer field. 

In addition to being popular, Pascal is excel- 
lent for programming. Adventure game writing in 
Pascal is a satisfying experience as I hope to dem- 
onstrate. The features of the language greatly 
simplify the process of translating an adventure 
game into computer form. The existence of the set 
as a primitive data type in the Pascal language 
makes implementing the carry and drop commands 
almost a snap. That is just one example. Many more 
will appear during the course of our investigation. 

Even though Pascal has been standardized by 
the International Standards Organization through 
the efforts of the British Standards Institute, the 
Joint ANSI/IEEE Pascal Committee in the United 
States, and computer standards committees in 
many other countries, there remain a large number 
of incompatible implementations of the language. In 
the microcomputer world, one implementation in 
particular has become immensely popular. That is 
the UCSD (University of California at San Diego) 
Pascal P-System implementation. It is not just a 
translator for the Pascal language, but an entire 
software development environment for use on 
micro and minicomputers. I shall use the UCSD 
implementation for all the examples in this book. If 
you use a different version of Pascal, you may have 
to make minor changes to implement the programs 
found here. 

I am going to assume that you are familiar with 
Pascal. You should know how to write Pascal pro- 
grams and should have written at least one or two of 
your own. You should have a textbook or reference 
that you can consult regarding matters of language 
syntax. I shall not attempt to teach the Pascal 
language from the ground up. There is a discussion 


of prerequisites in more detail in Chapter 4. 

As you read this book, you will learn new 
programming techniques in Pascal. You should 
study the sample programs carefully—reading 
programs is almost as good a way to learn pro- 
gramming as writing programs is. Then you should 
reread the programs and try modifying them in 
simple ways. Finally, you should attempt to write 
your own adventure games using the techniques 
described. 


ORGANIZATION OF THE BOOK 
The book itself consists of five sections: 


1. Adventure Game Concepts and Design— 
Chapters 1-5. 

2. Adventure 1: Simple Pascal for Adventure 
Games—Chapters 6-11. 

3. Adventure 2: A Complete Adventure Game in 
Pascal—Chapters 12-18. 

4. MAKEDESC and BROWSE: A Simple Da- 
tabase for Adventure Games—Chapters 19-25. 

5. Adventure 3: Advanced Adventure Game 


Techniques in Pascal—Chapters 26-30. 


Section 1 deals with general adventure game 
ideas. You should read it regardless of your level of 
experience in Pascal. Chapter 4 tells you about 
Pascal and the UCSD system and should help you 
decide how much you need to know before starting 
serious study of the book. 

If you are a beginner at Pascal, you should read 
all the sections carefully. If you are an intermediate 
Pascal programmer—you have taken a course in the 
language or have programmed in Pascal for at least 
a year—you may skim Section 2 and start reading in 
detail in Section 3. If you are an experienced Pascal 
programmer—two years or more of programming 
in the language —you may skim Sections 2 and 3 and 
start serious study with Section 4. If you have some 
UCSD experience, you may not need to read 
Chapter 17, which deals with the effective use of 
the UCSD system for developing Pascal programs. 

The appendices of the book treat miscellane- 
ous topics and include listings of the text of the 
descriptions databases used in Adventures 2 and 3. 


The Elements of Adventure Games 


In this chapter, I will discuss some of the ingre- 
dients necessary for creating adventure games. I 
will concentrate on games in the style of the original 
adventure; however, the techniques I describe 
apply to any adventure game. 

A typical adventure game contains certain key 
stylistic ingredients that stamp it as an adventure. 
Among these are the following: 


@ Rooms or Locations 


Part of the raison d’etre of adventure is to explore 
a cave or similar adventure territory. Hence the 
rooms of the cave or the locations of the adven- 
ture territory and their interconnections form an 
important part in the design of the adventure. 


@ Objects and Treasures 


Most adventure games contain treasures. One of 
the objectives of these games is to locate the 
treasures and bring them to some “safe” place in 
the game map. Creating interesting treasures 


and hiding them in unusual ways adds to the 
excitement of inventing and playing new adven- 
tures. 


In addition to treasures, other objects may play a 
part in a typical adventure. For example, in the 
original adventure there are many objects that 
are not treasures but are necessary in order to 
make progress in the game. In this game, for 
example, both a little bird and a cage must be 
dealt with. It is the adventure writer’s obligation 
to constantly invent new twists to the nature and 
use of objects. 


@ Beings and Monsters 


Adventure games usually contain other beings 
that the player may encounter. Usually these 
beings are adversaries, such as the fierce green 
snake, the troll, the dragon, and the pirate of the 
original adventure. However, there is absolutely 
no reason why a being in an adventure game could 
not be an ally as well as an adversary. 


@ Special conditions and Problems to solve. 


Part of the difficulty in winning adventure games 
lies in the problems that are incorporated. The 
player does not merely locate the treasure, carry 
it out, and win! On the contrary, various obsta- 
cles must be overcome. Many times ‘dealing with 
these obstacles is a prerequisite even to locating 
a given treasure. Here is where the full scope of 
creativity and originality come into play in mak- 
ing a good adventure. 


There are various styles of “problems” in ad- 
venture games. Some players relish a fight in 
which there are odds of losing—for example, the 
dwarfs in original adventure, who, if you are 
careless, can kill you. On the other hand, some 
players prefer a strictly logical challenge — 
problems that can be solved totally with the in- 
tellect and involve no chance factors at all. 


Now that I have enumerated the typical com- 
ponents of an adventure game, I will go into more 
detail in each category. The discussions below are 
intended to start your own creative processes 
going. They are not intended as a stock from which 
you merely choose a new combination to create 
your own adventure. To be truly successful at 
writing adventure games, in Pascal or whatever 
language, you must exercise your own individual 
creative powers. A definition of creativity that I 
especially like is the following: 


To be creative—look around you at what 
everybody else is doing. Then don’t do that! 


ROOMS OR LOCATIONS 


The classical environment for an adventure 
game is an underground warren of caves, tunnels, 
or mazes. There is something romantic about 
exploring an underground empire, looking for trea- 
sure. Many variations on the cave theme are possi- 
ble. It is still possible to be creative by inventing 
new and unusual rooms. To take an analogy from 
music, Bach, Mozart, and Beethoven all used the 
same musical scales and keys to create their musi- 


cal works. However, each composer produced art 
that was radically different from the other. There 
are many underground worlds still left to be in- 
vented that are as different from original adventure 
as Beethoven was from Bach! 

What then are some general guidelines for 
creating the rooms of a new adventure? Let us 
consider a few: 

Make Sure There is Variety. There should 
be a difference in character from one location to the 
next in a good adventure. There should be large 
rooms and small rooms. There should be rooms 
with lots of interest and others that are merely 
stops along the way. There should be objects 
spread around in a variety of rooms. It is probably a 
bad idea to put all the objects in one or two locations 
that are off in some distant and obscure corner of the 
map. The player of an adventure game likes to see 
progress being made. 

By the same token, not all locations should be 
easy to find. Not all objects and treasures should be 
located right out in the open. Thus, even when the 
player reaches a given room, there may still be 
aspects of that room that are only revealed after the 
player solves a problem or two. 

A good adventure map will have large ter- 
ritories that are easily accessible. It will also have 
one or two “chunks” of locations that are separated 
from the rest of the map by a narrow access path. 
There are various ways to achieve this goal: 


@ Make the only entrance to the “chunk” via a room 
that has a large number of exits. This means that 
the player has to try a large number of exits 
before finding the right one. 


@ Guard the only entrance to the “chunk”. That is, 
require that the player solve some sort of prob- 
lem in order to gain access to the entrance. An 
example of this sort of approach is the use of the 
fierce green snake in original adventure. It 
guards the only passageway to a large part of the 
cave. The only way to get through is to dispose 
of the snake. 


@ Make the first part of the “chunk” dull and boring 
so that the player is less likely to explore further 
in that direction. 


Require Exploration. Don’t create rooms in 
which everything is obvious from the very first 
description. There should always be rooms in 
which it takes some work to find out everything 
there is to know about them. There are many ways 
to accomplish this end. Some of them will be de- 
scribed later in this chapter. Here are just a few of 
them. 


@ Require that a special command be given in order 
to obtain the full description of the room. There 
might be hints that this is necessary, either in the 
ordinary description of the room or in some other 
aspect of the game. 


@ Require that certain conditions be met before 
giving the full description of the room or its 
contents. For example, the adventurer might be 
forced to possess a certain object or have ac- 
complished a certain goal before being able to 
fully discern the nature of a given room. 


@ Require the discovery of information about a 
room from someplace else in the adventure. For 
example, a se ‘et map giving necessary infor- 
mation might be found somewhere totally re- 
mote from the location in question. 


OBJECTS AND TREASURES 


A typical adventure game has a myriad of trea- 
sures that must be located. It also has a variety of 
objects, some useful and some merely window 
dressing. To be different, you might try an adven- 
ture that has just a single treasure. But, usually it 
will be wise to stick to tradition. In order to invent 
interesting treasures to find, you must simply be 
creative. As mentioned above, this involves in- 
venting treasures that no one else has used before. 
Likewise, there are millions of objects that have 
never played a role in an adventure game. So being 
creative should be easy! 

It should go without saying, but let’s say it 


anyway: 


Treasures should have some value. 


This need not be an intrinsic value, but might be 


value derived in some way from the circumstances 
of the game. For example, the game might be set in 
some imaginary kingdom in which dandelions were 
of inestimable value. In sucha situation, dandelions 
would be a legitimate treasure. Of course, it would 
be up to you, the adventure writer, to establish the 
value of dandelions. By inventing imaginary 
worlds, anything could potentially become a trea- 
sure. The ring of truth comes from the manner in 
which you set about convincing the adventurer that 
something ordinary could be of value in an imagi- 
nary setting. There is considerable scope here for 
inventiveness. As a challenge, try imagining 
something that you would ordinarily consider to- 
tally outrageous in the role of a treasure. Then try 
to invent an adventure setting and a description that 
makes that something utterly invaluable. 

Objects are even easier to deal with than trea- 
sures. There are no requirements at all concerning 
objects. An object may be present simply because 
you will it to be so. No other explanation is neces- 
sary. In fact, it is your obligation to have at least 
some objects that have no use in the game what- 
soever. Unless, of course, you are a classical purist 
who requires that every element of a game have a 
purpose no matter how small. Most of us are not 
purists and are quite willing to populate our adven- 
tures with stray incidental artifacts. It makes the 
game more challenging: the adventurer must dis- 
cover, by reason or chance, which objects are useful 
and which are not. 


Hints for Inventing Objects and Treasures 


@ Browse through the dictionary and the ency- 
clopedia. You will come across an amazing 
amount of material in these works. 


@ Play other adventure games. You may get ideas 
by enlarging on what has been done before. Try a 
new variation on old themes. 


@ Read fantasy and science fiction novels for ideas. 
Don’t plagiarize, but let your mind roam and wan- 
der. Start with what you read and extend by 
making new hypotheses and asking “What if?” 


BEINGS AND MONSTERS 


Good fiction contains good characters. Good 
adventure games will have interesting creatures 
inhabiting them. Here again, you may allow your 
imagination to run wild as you dream up monsters 
and other creatures to put into your games. 

There are some practical considerations here, 
however. 


@ Beings, in general, may move around from loca- 
tion to location. The creation of moving beings is 
generally more difficult than the creation of 
creatures that stay in one location throughout 
the game. 


@ Animate beings exhibit behavior. Places or loca- 
tions do not. It is a great challenge to incorporate 
behavior and reactions of creatures in your game. 


Most adventure games are limited in this regard. 


In this treatment, I will have creatures whose 
behavior patterns are somewhat limited. Doing a 
really serious job of simulating behavior would re- 
quire that I delve into advanced topics such as 
artificial intelligence, which I do not have the space 
to do. 


PROBLEMS 


To many players, it is the problems in a good 
adventure game that provide the true pleasure of 
playing them. I shall devote an entire chapter to this 
topic as I proceed. One aim will be to discuss not 
only the ingredients that go into creating good 
problems, but also the programming techniques 
needed to bring problem solving to life in Pascal 
adventures. 


Notations for Describing Adventures 


When you are creating an adventure game, it helps 
to draw maps. The adventure map summarizes the 
game in a concise notation that helps keep you 
organized. In this chapter, I describe my own nota- 
tion—the one that I use in the maps reproduced 
herein. You may end up adopting a system totally 
different than mine. That is perfectly acceptable. 
The idea is to have some way of describing your 
own adventure games. 


ROOMS OR LOCATIONS 


Most of any adventure map consists of the 
rooms or locations in the adventure. They should be 
laid out on paper in a rough representation of their 
“actual” geographic relationships. You obviously 
have to use some conventions here, especially for 
dealing with up and down. 

Pick a standard symbol for representing a lo- 
cation and always write the name of the location 
inside the symbol. I like the hexagon or elongated 
hexagon shape for most rooms, with circles or ovals 
for maze rooms or other crowded locations. You 
may also choose to use different symbols depending 


on the nature of the location. For example, a rec- 
tangle, as opposed to ahexagon, might represent a 
room containing a treasure. 


TRAVEL INDICATORS 

Adventure locations would be quite unin- 
teresting if it were not possible to travel between 
them. Adventure maps show ways to travel be- 
tween locations. Each possible path of travel may 
be indicated by a bold line joining the two locations. 
Arrowheads at the ends of the lines may be used to 
indicate whether or not the direction of travel is 
reversible. The absence of an arrowhead means 
that it is not possible or permissible to travel along 
this route in the direction indicated by the missing 
arrowhead. 

Directions are part of the descriptions used in 
the play of the game itself. Each line of travel should 
be marked with a direction indicator. The usual 
directions are limited to n, s, e, w, u, and d. Occa- 
sionally, a minor compass point, such as NE or SW 
may be used. 

Some travel paths may only be taken when 


5 


certain conditions are fulfilled. Some examples of 
this are 


@ The player is carrying a certain object. 
@ The player has solved some problems. 
@ The player has removed an obstacle to travel. 


These conditions may be indicated on the map 
by a brief description of the condition in words. This 
description can be written near the affected travel 
line or can be placed in a footnote. 


PROBLEMS 


Much of the fun of adventure games lies in the 
problems they pose for the player to solve. I elabo- 
rate on this theme in a future chapter. For now, I 
only want to show you how to indicate that prob- 
lems exist in various places on the adventure map. 

Problems may be indicated by a special sym- 
bol. I use a balloon letter style question mark, 
enclosed in a circle as shown in Fig. 2-1. 


? 


Fig. 2-1. A problem indicator for adventure game maps. 


This notation needs supplementation in order to 
distinguish one problem from another. One way is 
to assign a number to each of the problems in the 
adventure. Write the number of each problem near 
the appropriate question mark symbol. In a sepa- 
rate place, describe each problem in detail in 
words. Another possibility is to give each problema 
name analogous to the placenames given to loca- 
tions. The name of the problem could then be writ- 
ten near the ? symbol instead of a number. I use the 
numbering approach and write the number of the 
associated problem inside another full circle placed 
to the right of the circle containing the ? symbol. 


OBJECTS 
Objects usually start out at a fixed location at 


the beginning of the adventure. All such objects 
may be indicated on the map by writing the name of 
the object inside the box designating that location. 
Of course, this notation describes the adventure at 
the start of play. The player is free to pick up 
objects and move them around during play. 

To indicate that there is a problem associated 
with an object, you can place a dash after the name 
of the object. Then a small ?, perhaps followed by 
the number of the problem, can be written after the 
dash. Occasionally, an object will have properties 
that are not part of the description of any problem. 
This situation can be handled by the use of footnotes 
that associate the textual description of the proper- 
ties with the name of the object itself. 

You may like drawing a cartoon style cloud 
around the name of each object. This serves to 
make a graphic distinction between the placename 
and the object name. While this is not strictly 
necessary, some may find it esthetically or psy- 
chologically pleasing. 


BEINGS AND MONSTERS 


These may be considered to be objects. They 
are animate objects, true. However, when you are 
drawing the adventure map, you can treat them just 
as you treat the inanimate objects. 


EMBELLISHING THE MAP 

The artistically minded may embellish their 
adventure maps much further than these simple 
instructions indicate. Pictures of locations, mon- 
sters, and objects all make interesting viewing. 
Perspective drawings of the locations and their re- 
lationships is also fun. All of this has nothing per se 
to do with programming adventure games, but their 
creation may well stimulate the imagination. 


MAP LAYOUT AND ORGANIZATION 


For complex and detailed adventure games, 
you will find that you have to draw a large map. You 
won't be able to fit this map on a single sheet of 
standard sized paper. If you don’t want to go to the 
trouble of locating and purchasing oversized draw- 
ing paper, you will need to split your maps into 
multiple sheets. When you do this, try to group 


your locations into geographically and/or logically 
related “clumps.” Then put one clump per page on 
your maps. Try to choose the clumps so that there 
is a minimum of possible travel paths between the 
different clumps. Then the connections between 
pages may be indicated with a symbol often used in 
drawing complex computer program flowcharts. 
The symbol in question is appropriately named the 
off-page connector. It looks like a pentagon with a 
number in it. The numbers in the pentagons are 
used to associate two connectors on different 
pages. They do not refer to any numbering of the 
map pages themselves. If a straight line leads into 
an off-page connector, look for a similar connector 
on another page. The matching connector should 
have a straight line leading out of it as shown in Fig. 
2-2 


—— 
[3 


Fig. 2-2. An off-page connector for adventure game maps. 


EXAMPLES 

Each adventure game included in this book is 
mapped in detail using the techniques of this chap- 
ter. The resulting maps are included in the preview 
chapters that precede the complete listing of the 
Pascal source code for the game itself. 


Adventures and Problem Solving 


The essence of a good adventure lies in the prob- 
lems it poses for the user to solve. They must be 
varied, original, interesting, challenging without 
being impossible, intricate without being arbitrary, 
and so on. This is truly an area where your creative 
impulses may be vented to their fullest. There is no 
cookbook approach to creating good problems. 
There are a number of guidelines, however. Let’s 
do some exploring and see what we can discover. 


CLUES TO PROBLEM SOLUTIONS 


For each problem you contrive in an adven- 
ture, there should be at least one associated clue or 
hint. Such clues may take many forms: 


@ They may be hidden in the description of loca- 
tions. Such clues should be subtle. In most cases 
the hint should be indirect. The player should be 
required to make some deduction based on the 
clue and other information. Only in .are cases and 
for extremely trivial and unimportant problems 
should you give away the answer directly. 


The player should have some way of telling that 
part of a description is in fact a hint. This can be 
done by making the hint somehow different in 
tone or style. Make it just slightly “jar” the 
player into recognizing it. 


@ Clues may be written on objects contained in the 
adventure. A clue may be written on a wall ona 
piece of parchment hidden in a closet or an old 
chest, on the bottom of a bottle, or just about 
anywhere. It may take an explicit command on 
the part of the adventurer to get at such clues. 
This gives you a chance to give hints about hints! 
You can take this more than one level deep in 
some cases. A few problems may even require an 
entire chain of clues in order to reveal their solu- 
tion. 


To get at a message hidden inside a container, 
the player should have to open the container, 
take out the object on which the message is 
written, and explicitly ask to read the message. 
How all this is represented in a computer pro- 


gram will be discussed later. Keep in mind that 
you will have to program your clues—so don’t 
get too carried away. 


@ Clues may be implicit in the behavior of beings or 
entities within the adventure. The way a monster 
reacts to certain commands or situations may 
reveal weaknesses that the adventurer can 
exploit. 


When you think of a good problem to include in 
your adventure, don’t stop there. Work on ways of 
making the problem solution entertaining for the 
player. Really good clues will enhance this. Don’t 
expect all this to magically come to you the instant 
you think of a problem. You should let the problem 
roll around in your subconscious for awhile. Write 
each problem ona separate sheet of paper and make 
notes as ideas come to you. Gradually crystallize 
these ideas into hints and clues. As you develop 
your adventure story line and structure, come back 
to these sheets again and again. Ask yourself if you 
can think of more or better clues. The effort you put 
into this will pay off in compliments from those who 
play your games. 


PROBLEM DIFFICULTY: FROM 
THE OBVIOUS TO THE OBSCURE 


Not all problems in an adventure should be 
equally difficult or easy. There should be a variety 
of levels of difficulty. There should be enough rela- 
tively easy problems in all parts of the game to keep 
the player coming back to the harder problems. The 
surest way to kill off interest in all but the most 
stubborn adventurers is to make all your problems 
next to impossible to solve. 

How can the difficulty of a problem be esti- 
mated? One way is by the number of clues needed in 
order to solve the problem. The more information 
that must be gathered in order to figure out the 
solution, the harder the problem is likely to be to 
solve. Another factor is the individual clues them- 
selves. A problem that has one very obscure clue is 
probably harder to solve than one that has several 
easy clues. 

When you start writing real adventures, get 


others to play them. Then get feedback on the 
difficulty levels that you have put into them. If you 
have friends that are also writing adventures, so 
much the better. Trade adventures back and forth 
and then trade ideas about making problems both 
interesting and realistic in terms of their level of 
difficulty. 


REPEATABILITY 


There is a choice to be made in creating ad- 
venture problems. Do you want the solution to a 
problem to have an element of chance or not? For 
example, if you have to fight other beings, should 
there be an absolutely guaranteed method of win- 
ning or should there be some probability, however 
small, of failure even in the presence of perfect 
logic? This is a choice that you, the adventure 
writer, will have to make. It all depends on who will 
play your games and what their preferences are. 

Absolutely repeatable problems will involve 
only logic in their solution. Problems that involve 
chance may still involve logic as well. The differ- 
ence is that the player is not in complete control of 
the outcome. There is a middle ground here. You 
may create a problem in which the player may take 
certain actions in order to guarantee that the chance 
element is ruled out. For example, if the player 
attacks acertain monster first, that might guarantee 
that there is a completely logical way to avoid being 
a victim of the monster. Again, it may require other 
kinds of actions on the part of the adventurer to 
secure such results. The player may need to get 
killed off a few times in order to notice what these 
conditions are. Here you have another way of 
creating wheels within wheels effects. The player 
may through experience learn more and more about 
how to solve a problem. There may be progress 
through several levels where chance still plays a 
part, before the player reaches the absolute logical 
pinnacle consisting of the problem solution. 


LOGIC 

Good problems should allow a player’s true 
problem-solving ability to be exercised. The solu- 
tion to a problem should not be completely arbi- 
trary, requiring the player to make wild or random 


9 


guesses in order to arrive at a solution. In short, 
problems should be logical. 

What is logic? For our purposes, it is a system 
of reasoning. The player may draw conclusions 
about the play of the game. Such conclusions may be 
based on information contained directly in the 
game. This may take the form of hints as discussed 
above. It may be hidden in descriptions of locations 
or objects. On the other hand, it may be necessary 
to draw conclusions based on common sense; that 
is, based on the normal properties of objects or 
behavior of objects in a natural setting. For exam- 
ple, water makes plants grow; dry wood may be set 
on fire with matches; doors must be opened in order 
to see what is behind them; and you may have to dig 
in order to uncover buried treasure. 

In deducing information, it may be necessary 
for a player to make guesses. These guesses should 
always be reasonable, not arbitrary. And good 
guesses might be rewarded with the offer of further 
information. 

A good way to require the use of logic is by 
including more than one hint or clue regarding a 
problem’s solution. The player must discover two 
or more clues and connect them logically in order to 
solve the problem. 


SURPRISE 


I have just finished emphasizing that problems 
should have logical solutions. Having said that, I 
can now relax my position a bit and allow for the 
element of surprise. 

There may be an inherent conflict between 
logic and surprise. What is totally logical is not 
surprising. What is surprising cannot be totally 
logical. What you wish to avoid is not illogical solu- 
tions, but arbitrary ones. 

There is a fine line between a surprising solu- 
tion to a problem and an arbitrary one. To illustrate 
this point, consider the original adventure game. In 
that game there are two problems that serve as 
perfect examples. First consider the one with the 
surprising solution. 

There is a fierce green snake that bars you 
from entering a certain passageway. This tunnel 
turns out to be the only entrance to the rest of the 


10 


cave. Therefore you must find a way to get rid of the 
snake. If you have played the game “correctly” up to 
that point, you reach the location of the snake car- 
rying a cage containing a timid little bird. You try 
everything you can think of to get by the snake, but 
all efforts fail. What to do next? Now, using logic, 
you might engage in the following sort of dialogue: 


Q. What means are at my disposal? 

A. None. 

Q. Really? 

A. Well, I do have this bird in the cage. 

Q. What might you do with it? 

A. I could get it to sing—maybe that would 
frighten the snake away. 

Q. Oh well, I suppose. Go ahead and try it. 

A. OK. 

Q. What happened? 

A. Absolutely nothing. 

Q. Well, what else can be tried? 

A. I guess I could let the bird go. Maybe I could 
catch the snake in the cage. 

Q. Good idea. Go ahead and do it. 

A. OK. 

Q. What happened? 

A. Amazingly enough, the bird drove off the 


snake! 


Surprise, surprise, surprise! You never thought the 
bird could have any positive effect on the snake. 
Nonetheless, you did discover the solution by a 
process of reasoning and common sense. (You 
thought the cage might be useful.) 

Now for the problem with the arbitrary solu- 
tion. When you get extremely good at the original 
adventure, you find that you have accumulated 349 
points out of a possible 350. Unless you stumbled 
on the way to get the 350th point, you are now faced 
with the nasty problem: “How do I get the last 
point?” This problem is particularly nasty since its 
solution could lie almost anywhere. Some people 
spend hours and hours, for example, looking for a 
location that they have not visited that might net 
them the extra point. 

There are some magazines that exist in the 
game. They seem to play no role. However, if you 


pick them up from their original location and carry 
them to Witt’s End, you get the extra point! How 
utterly disappointing this turns out to be when you 
discover it—truly arbitrary, truly deflating, truly 
anticlimactic. 

The moral of the story is—try to avoid totally 


arbitrary solutions. Don’t make players of your 
game guess that your Uncle Harry’s hair is brown in 
order to get a point! Don’t require them to perform 
random acts in random locations in order to secure 
the last point! 


11 


UCSD Pascal Review 


All of the programs presented in this book were 
written using the UCSD Pascal system. I used the 
version of this system as adapted by Apple Com- 
puter Co. for use on the Apple II and Apple J//e. 
These programs should work with little if any mod- 
ification on any computer using the UCSD P- 
System. 
In this chapter I intend to accomplish the fol- 
lowing: 
@ Tell you what you should know about Pascal 
before digging in further. 
@ Explain to those that are unfamiliar with it what 
the UCSD System is. 
@ Highlight some of the features of the UCSD Sys- 
tem and some of the tricks of using it effectively. 
@ Give you some general suggestions that will be 
helpful when you start writing your own adven- 
tures. 


WHAT YOU SHOULD KNOW ABOUT PASCAL 


This is not an introduction to Pascal program- 
ming. I am going to assume that you know the 


12 


basics. That means that you have written at least a 
few Pascal programs. It means that you are familiar 
with the syntax of Pascal and the mechanics of 
writing programs. You should at least have an idea 
of the meanings of the following terms: 


@ Procedures and functions. 

@ Block structure. 

mw Parameters: actual and formal. 

@ Types (user-defined, predefined), type checking. 
@ Structured statements, control structures. 

@ Declarations. 

wm Value parameters, var parameters. 

m Files (sequential), get, put, readln, writeln 

@ Arrays. 

@ Records. 


I will be using most of these concepts as well 
as some others in the programs. In many cases, I 
will give detailed explanations of how a particular 
usage works, and why it makes sense in Pascal. 
Thus, if you are not an expert, you should be able to 
learn more about how to use many Pascal features. 


see also page 143, “The #*%%!!? ESCAPE Key” 


Just study the example programs diligently and try 
to follow the explanations. 

I won't start out with the most basic topics, 
however. That is why I hope you are already con- 
versant with these concepts. 

If the preceding list makes you feel tired or 
intimidated, you should probably obtain a good be- 
ginning textbook on Pascal. Read it in parallel or 
slightly ahead of your study of this book. There are 
many books already out on Pascal and many more 
appearing all the time. The best places to seek them 
out are your local computer store or bookstore. 
When you buy a book make sure that it is not limited 
to a subset of Pascal or to an implementation that is 
peculiar to one or two computers. 


WHAT IS THE UCSD SYSTEM? 


The UCSD Pascal system was originally de- 
veloped at the University of California at San 
Diego. It was intended to be used in the teaching of 
Pascal, and the goal was to implement the system 
on a variety of small computers, including mi- 
crocomputers. The project was conceived and di- 
rected by Dr. Kenneth Bowles, a professor of com- 
puter science at UCSD. 

The system is just that. It is a complete 
software development environment for Pascal, in- 
cluding not only a Pascal compiler but also a com- 
plete operating system and many utility programs. 
When you use the UCSD system, it is in complete 
control of your computer. Thus, if you have a sys- 
tem with CP/M, you must reboot the system in 
order to use UCSD Pascal, and you no longer have 
access to CP/M. The same is true for most micros 
that support a manufacturer’s proprietary operating 
system. For example, when you run the UCSD 
system on the Apple II, you must forego access to 
the Apple DOS. 

The UCSD system has been made into a com- 
mercial product by the SofTech Microsystems 
company in San Diego. It is now referred to as the 
P-System, and it runs on virtually every mi- 
crocomputer currently manufactured. The system 
is based on an interpreter that executes a pseu- 
code known as P-Code (whence the name P- 
System). The interpreter communicates with the 


hardware and the peripherals of the computer via a 
small set of programs known as the BIOS (Basic 
Input Output System). 

The BIOS includes the machine code neces- 
sary for booting the system from a floppy disk. The 
booting process involves the reading in of the BIOS 
and the P-Code interpreter from the floppy disk. 
Then the operating system portion of the UCSD 
system is loaded. It is written in P-Code and is 
interpreted by the P-Code interpreter. Once it has 
been loaded, control is passed to the interpreter, 
and off you go. 

The UCSD system is one of the early menu- 
driven systems. A menu isa list of choices coded by 
numbers or letters from which the user chooses. 
The UCSD system has a command line that lists the 
commands available ina given context and indicates 
their abbreviations. You have to know what each 
command will accomplish by reading the users’ 
manuals, because there is no online help facility 
available. 

In addition to the BIOS, the P-Code interpre- 
ter, and the operating system, the UCSD system 
provides 


@ A full-screen text editor. 

@ A filer for manipulating files on the floppy disk. 

g A Pascal compiler for compiling Pascal programs 
into P-Code 

@ A linker for combining certain P-Code files into 
programs that are executable by the P-Code in- 
terpreter. 

@ A librarian program that enables you to create 
libraries of reusable pieces of P-Code. These 
pieces may then be incorporated into many dif- 
ferent programs. 


There are other features of the UCSD system, 
but these are the ones that I will be using the most. 

In order to learn about the UCSD P-System, 
you will need the users’ manuals for your particular 
implementation. I use the manuals for Apple Pascal 
as provided by Apple Computer. There are also 
textbooks available that discuss the use of the sys- 
tem, although they tend to be paraphrases of the 
UCSD documentation, organized differently and 
with different diagrams. Still they may in many 


13 


cases be useful—you will have to decide for your- 
self. 

This book is not intended to be a tutorial on the 
UCSD Pascal system. I assume that you know 
enough of the basics to use it. I will, however, give 
instructions on some techniques that you may not 


14 


have picked up in your beginning use of the system. 
This will take the form of tips about general use of 
the system and some detailed instructions on pre- 
paring the programs in this book for actual use on 
your computer. 


Preview of Adventure 1 


In this book, I present substantial Pascal programs. 
Adventure games in Pascal tend to be large pro- 
grams; there is no getting around that. My approach 
will be to introduce you to the goals and content of 
each program with a preview chapter. Following 
the preview you will find the complete listing of the 
Pascal program itself. After that will be several text 
chapters explaining the design of the program, the 
Pascal features used, and the way the Pascal pro- 
gramming techniques relate to the adventure game. 

In order to get the most out of each program, 
here is how I suggest you read this book: 


w Read the outline chapter, noting the mention of 
any Pascal features with which you are not al- 
ready familiar. Pay particular attention to the 
outlines and diagrams. Try to grasp the overall 
design of the program. 

gw Skim through the program listing in back to front 
fashion—see the explanation of how to do this in 
the preview of Adventure 1 later in this chapter. 
Write down questions you may have about how 
the program works. Refer to these questions as 


you study the program in detail later on. 

Read and study the chapters devoted to explain- 
ing the program. As you read, refer frequently to 
the actual program listing. If possible enter the 
code for the program in your own computer as 
you proceed. 

@ After reading all the chapters pertaining to the 
specific program you are studying, go back and 
read the listing of the program once again. This 
time, read the program for details. Make notes of 
the particular coding techniques that you plan to 
use in your own adventures. 

@ Finally, design your own adventure game, using 
the techniques illustrated by the program you 
have finished studying. Implement it using your 
own computer and UCSD Pascal system. Com- 
pare your finished game to the illustrative pro- 
gram in the book. Review your lists of questions, 
and see how many you now can answer yourself. 


PREVIEW CHAPTERS 
Long programs can be intimidating, confusing, 


15 


and difficult to understand. So I would like to give 
you a perspective of a program before plunging into 
its details. By starting out with an idea of the overall 
structure of a program, you will be able to under- 
stand it more quickly. You will be able to fit details 
into the overall picture. Concentrating on program 
structure from the start will also encourage you to 
pay attention to the structure of the Pascal pro- 
grams that you write yourself. This is an important 
point often overlooked by beginning or relatively 
inexperienced programmers. 

I will include diagrams that show the static 
structure of the program. These diagrams will list 
the program elements (declarations, functions and 
procedures by name, main program, and so on) in 
the order in which they appear in the program list- 
ing. In addition to the static structure diagrams, I 
will include diagrams for each program revealing its 
dynamic structure. These diagrams will be mod- 
eled after the style of diagram used in the structured 
design technique of programming. Such di- 
agrams reveal the relationships between the vari- 
ous procedures and functions in the program. They 
may also show how various pieces of data used by 
the program are passed around and modified by dif- 
ferent parts of the program. 


(BD 


Fig. 5-1. A diagrammatic representation of a procedure call in 
structure diagrams. 


16 


CAD 
; 


Fig. 5-2. Data connection in a structure diagram: A passes C 
to B. 


Each procedure or function (as well as the main 
program itself) is represented in a structure dia- 
gram by an oval. The name of the procedure or 
function is written inside the oval. Lines with ar- 
rowheads connect various ovals in the diagram. A 
line that points from an oval named A to an oval 
named B, as shown in Fig. 5-1, indicates that pro- 
cedure or function A calls procedure or function B. 
That is, the procedure or function A has in its 
program text (or code, if you prefer) an invocation 
of procedure or function B. Ifthe tail of the line (the 
end without the arrowhead) has a diamond shape, 
@, it means that the call from A to B is conditional. 
The code in A will make some test. Depending on 
the outcome of the test, A may or may not call B. 
This information can be valuable in debugging a 
program; if there is a conditional call to B, but B is 
not called when it should be, there is no doubt a 
problem in the logic of A’s code. 

Some lines that connect ovals will be labeled 
with variable names. This means that the data rep- 
resented by that variable passes between A and B. 
If A provides B with a variable named C, the dia- 
gram might look something like the one shown in 
Fig. 5-2. 

Notice the smaller arrow beside C. The circle 


PROGRAM miniadventure; 


CONST, TYPE, and VAR declarations: 


This section of the program contains the declarations 
of the constants, types, and variables used by the 


rest of the program. 


See Figure 5-4 


procedures and functions: 


This section consists of all the headers and code for all 


the procedures and functions accessible to the 
main program block and the outermost level 
of the program. 


See Figures 5-5 and 5-6 


This section of code is the first to be executed. 
It is referred to as the main program block. When reading 
a Pascal program, you should always start here. 

See Figure 5-7. 


Fig. 5-3. The code outline for Adventure 1: the program outline. 


on the tail of this arrow points toward the provider 
of C, in this case A. This information is also of 
potential importance. Many bugs in programs are 
due to variables somehow obtaining incorrect val- 
ues. A structure diagram can help reveal where a 
variable could be “going sour.” Careful study of the 
diagram with your code at hand often reveals prob- 
lems. 

Structure diagrams give another viewpoint of a 
program, one that is more important in under- 
standing how the code works. When you study 
structure diagrams, notice how groups of functions 
and procedures form logical chunks of a program. 
Notice also how each procedure and function ac- 
complishes one logical task within a program. As 
you program in Pascal, this use of logical structure 
should be a conscious goal. It makes debugging and 


program modification considerably easier. 

In addition to all the diagrams that attempt to 
reveal the structure of the program, I shall include a 
survey of the chapters that follow the program list- 
ing. Each chapter will be described briefly. You will 
be given an idea of what to expect from each chap- 
ter. 


PREVIEW OF ADVENTURE 1 


The full listing of Adventure 1 is presented in 
Chapter 6. After you finish reading this chapter, you 
should read the listing of Adventure 1 in back-to- 
front style. What does that mean? In standard Pas- 
cal programs, the so-called main program block, 
that is, the code that is executed first, is located at 
the end of the program text. So when you read a 
Pascal program, you should really start at the back. 


17 


“Seunpeooid puke SUO!OUN} ay} : |, BINJUBAPY JO} aUII]NO apPOd aU! “S 


‘puapeap © ¢ ‘yd ‘zmoueu 
SUOIJEDO} AU} (WO |AACI} 


‘puapeapd 3Ynaa90ud 


Huipnjoul) ye uooe 9y} 
ajpuey yey} Sainped0lq 


dd 3una390ud 
‘~moueud 3YNG3I0Ud 


*9-G aunBi4 aag ‘saunpadoid puke suol}oUNn} 

W901 aiow Jo auo sey yoey ‘azew ay} pue 

uap S,a160 ay} ye UOH}Oe au} ajpueY yey} SaINp|ad01q 
‘azewd 34Nd390Ud 
‘woosas8od 34Nd3I0Ud 


‘woo1aaid 34NG390Ud 


“WOOJad! “°° ‘AINQNSEA ‘pes 
SUO!}E90] BY} (WO }@ACI} 
Buipnjoul) ye uoNoe au} 
ajpuey yeu} SAINpsd01q 


‘ainqnsaad 3uNda90Ud 
‘weysd 3UNdaI0Nd 


‘uap s,ai60 ay} ye UOH|Oe ay} Buljpuey ul s}sissy 
‘uonoeai80 3YNdIIOUd 


jsiayja] Sey puewWOd ains Sayew }| 

“@SOdNd Siy} JO} PUBWIWOD JO 19}}9] }Suly BY; Je 

SOO} }| “A¥2} 0} SAYSIM JaAe|d By} UOHDAJIP 4} SEUIWW9}9q 
‘suonoelp :AeMYoIYM NOILONN 


‘aweb au} Jo pua au} ye as00s SJjahejd ay} Sayejnojeo 
‘UFDILNI *2J09S NOILONN 


‘Ke\d jo 
Buiuuibaq ay} ye aweb ayy jo aye}s au} jOoyas SAN|eA 
jenul ayy “sajqeuen wesBoud ye 0) sanjea subissy 


‘azZiJeNu! JYNGIIONd 


‘apin6 ay} pue aweb au} 0} 1aAe|d ay} Seonposju| 
UONINPOAU! JYNDIIONd 


-¢ “Biy 


“suolyesejoap eyep ay} :Z aINJUSAPY 40} aUIIJNO apod aU, “p-S ‘BI4 


‘aweb ay} Bulunp paind90 OU eAey 410 sAeY 


UdIUM S]UBAP JO 494} daay 0} SajqeUeA 


payood ‘paddes ‘paddoip 
‘SuiAueo ‘Bswpeas ‘ayeme ‘uajea ‘jinb ‘auop 


sahejd 

ay} Aq uaye} SUN} Jo JaquUNU ay} S}JUNOD 
sun} 

0} pa}aaes} sey 

jakejd ayy yey} aweb ay} ul seoejd ay) 

soe} YoIUM ‘swoos Aq paexepul ‘Aeue ue 
Paysia 

payeoo| si saAejd 9u} 

a19UM SayedIpUl SOO! adAj jo ‘anjeA Ss} 
uolje 90} 

JoXejd ayy Aq pad} 

(@y42} 0} UO!}OaJIP) PUBWLWOD SpjOYy 
puewwoo 
wesBoid ay} Aq pasn sajqeuen 


JAAR} JO SUOIDAIIP ajqisSod au} S}juesesdas 
SuoljoauIp 


| unquaApy ul SUOI}e90| 34} s}Juaseideal 
swool 
sadA} pauyap-iasn 


s]ue}SuOd pauyap-sasn 


18 


PROCEDURE pogreroom; 
PROCEDURE general description; 


Local (or nested) procedure 
that is used to print 

the general description 

of the ogre’s demesne. 


PROCEDURE pmaze; 


Procedure that implements the maze. It is like a small 


adventure inside the larger adventure. It uses 
numerous local procedures and functions. 


FUNCTION bittest: BOOLEAN; 

PROCEDURE describe; 

PROCEDURE sameplace; 

PROCEDURE treasure; 
These procedures provide general 
support for the action in the maze. 

PROCEDURE pm1; 

PROCEDURE pm2; 

PROCEDURE pm3; 


PROCEDURE pm 18; 

PROCEDURE pm19; 
Procedures pm1, pm2, . . ., pm19 are the location 
procedures for the nineteen maze rooms. They are 
analogous to the location procedures pstart, pvestibule, 
and so on. They handle travel and action inside 
the maze itself. 


The last part of the listing will contain the main 
program block. The code there will invoke other 
parts of the program, in particular the procedures 
and functions whose text appears earlier in the 
listing. This means that as you see references to 
procedures and functions in the main program 
block, you will have to look for the listings of these 
procedures and functions closer to the front of the 
program. In turn, these procedures and functions 
may refer to other procedures and functions. Un- 
less the author of the program has used FORWARD 
declarations (which I in general attempt to avoid in 
my adventure games), the procedures referred to 
will be listed even closer to the start of the listing. 


Fig. 5-6. The code outline for Adventure 1: 
the procedures with local procedures. 


Now you can see what I mean by reading the pro- 
gram back-to-front. 

Figures 5-3 through 5-7 present the code out- 
line of Adventure 1. Figure 5-3 shows the entire 
program structure in brief, explaining the major 
parts of every Pascal program. It refers to the other 
figures that show various parts of the program in 
more detail. Figure 5-4 outlines the const, type, 
and var declarations of the entire program, giving 
brief descriptions of some of the more important 
declarations used by the program. Figure 5-5 out- 
lines the procedures and functions used by Adven- 
ture 1. Again, there are brief comments regarding 
some of the more important of these. Figure 5-6 


19 


BEGIN 


Procedure invocations (once only) at the 


start of the game play. 


introduction; 
initialize; 


REPEAT 
CASE location of 
start: pstart; 


grandroom: pgrandroom; 


Fig. 5-7. The code outline for Adventure 1: 
the Main program block. 
flames: pflames; 
END; 
UNTIL quit OR done; 


Main control loop of the adventure game program. 
Various location procedures are repeately invoked 
until the game is complete. This is signaled by one of 
the two Boolean variables quit or done becoming TRUE. 
congratulations; 

Procedure called to wrap up the game and summarize 


the results including the score for the player. 


outlines one of the longer procedures of the pro- 
gram, pmaze. This procedure is notable for having 
many local procedures and functions. Finally, Fig. 
5-7 outlines the main program block of Adventure 1. 

Figure 5-8 is the dynamic structure diagram of 
Adventure 1. It shows the relationships between 
various groupings of procedures and functions used 
by the program. 


CHAPTER PREVIEWS 


Chapter 7 is entitled “Representing the Map.” 
It discusses the use of enumerated types in Pascal 
programs generally an in adventure games specifi- 
cally. You should pay special attention to this brief 
chapter. It lays groundwork for a technique that is 
used repeatedly in later adventures. At the end of 
Chapter 7, the map of Adventure 1 is presented in 
the graphic format explained in Chapter 2. 


20 


Chapter 8 is entitled “Controlling the Play” 
and deals with the use of enumerated types in con- 
junction with Pascal case statements. Chapters 7 
and 8 go hand in hand. Go back and reread both 
chapters after you finish them the first time. The 
use of enumerated types in Pascal case statements 
is one of the most powerful Pascal coding tech- 
niques. Unfortunately, it is also one of the most 
underused. Master the technique, and you will have 
a valuable tool for all your Pascal coding, not just 
your adventure game writing. 

Chapter 9 is entitled “Mazes in the Middle” 
and deals with the implementation of the maze in 
Adventure 1. It discusses the use of local proce- 
dures and functions as well as local declarations in 
general. 

Chapter 10, entitled “Other Techniques Used 
in Adventure 1” discusses what I have termed the 
ad hoc code of Adventure 1. It explains how mis- 


congratulations 


oe 


eae 


Conver) Come) 
SIciIcIo 


ogrereaction 


= 


(em) 
a7 


MRT ee Bet eS 
pm11 pm12 pm13 pm14 pm15 


\wY WN” owas a 


pm16 pm17 pm18 pm19 
A / =" _ 


Fig. 5-8. The structure diagram for Adventure 1. 


cellaneous features of the game were implemented problem in a very specific way and may not be as 
by direct Pascal coding techniques. The techniques generally applicable as some of the techniques de- 
are ad hoc in the sense that they solve a specific scribed in earlier chapters. 


22 


Pascal Adventure 1 


This chapter presents the complete listing of the 
first Pascal adventure game. This example is meant 
more to illustrate features of Pascal than to be a 
serious game. To get the most out of this program, 
you should go through the following steps: 


1. Skim read the program right now. Try to grasp 
the overall structure of the program and don’t 
worry too much about the fine details. Refer 
back to the code outlines in Chapter 5 as you 
skim. Jot down any questions you may have 
about the overall organization of the program. 

2. Read and study Chapters 7 through 10, which 
discuss the details of the Pascal coding of this 
adventure. As you read these chapters, study 


LISTING 6-1. PASCAL MINIADVENTURE 


4, 


Before typing, see page 143, “The #*%%!!? ESCAPE Key” 


the listing of Adventure 1 and absorb the details. 
Imagine how you will apply the language fea- 
tures discussed to your own adventure game 
programs. 


. When you have finished Chapter 10, go back and 


reread the listing of Adventure 1 in detail. Check 
your list of questions from step 1, and see how 
many of them have now been answered. 
Write an adventure of your own using the 
techniques illustrated by Adventure 1. Make up 
your own map and your own location descrip- 
tions. Notice what parts of the Pascal code you 
can use almost unchanged and what parts you 
have to alter radically. 


(in addition to Chapter 4, page 12.) 
COCO OIC CK OK OOK OOOO CICK GIO GK GK) 


(x *) 
xX a dv @ nm t wor «& # 4 x) 
(xX x) 
(kK This is an example of the Pascal language x) 


23 


(*k features that are useful in writing advent~-  *) 
(kX games. It is not really a serious game, but x) 
(k more an instructional exercise. *) 
(x x) 
COO OOOO OK GOK OK KK KKK KKK ) 


CXHo+k) ——e Compiler “swap” instruction, see Chapter 17, page 141 
FROGRAM miniadventure; 


CONST 
fF = 12s 
ew = 12; 
nw = os 
ne = ben 
Sew = 14; 
nonly = 1: 
nsew = Les 
newud = O14 
Sw = 10; 
ns = 8 
danly = S24 
cin = aan § 
ud = 48; 
SU = 18s 
TYFE 
rooms = (start, grandroom, vestibule, narroawl, 
lakeshore, island, brink, iceroom, 
ogreroom, narrow2, pit, crystal, 
batscave, steam, deadend, ladder, 
maze, flames); 
Girections = (MaSaG@uWatta dd) § 
byte = O,.2553 
VAR 
commands STRING; 


(xk holds user typed direction *) 
chs CHAR: 


dchars: SET OF CHAR: 
(* characters which correspond TQ the 


24 


acceptable initial letters OF 


direction commands. initialized TQ 


ae 2 aa —- e  —  ' *) 
location: rooms s§ 
ogreloacs: rooms; 
visited: ARRAY C start..flamesJ QF BOOLEAN; 
nexts directions; 
twopow: ARRAY EO n..dJ OF INTEGER; 
turns: INTEGERS 
done: BOOLEANS; 
quits BOOLEAN; 
eaten: ROOLEANS 
awake: BOOLEANS 
readmsg: BOOLEAN: 
carrying: BROOLEANS 
dropped: BOGLEANS 
trapped: BOOLEANS; 
cooked: BOOLEAN: 


COOK KOK KKK) 

(kK wip © x) 

CK KKK KKK KKK KD 

FROCEDURE wipe; 

REGIN 
write (chr (ff)); 

END; 


CKPiminii.text*) These are “include” instructions; see Chapter 17, page 140 for more info. 


(XPiminiz.textxk) As written, they assume all files are on Drive 1 (Volume #4: in Pascal.) 


(Xtimazel.textx) For this and for page 81, refer to “A note about disk Drive numbers in file 
(XGimini2. text) names and commands” on page 3 of this PDF. 


OK OK A new file named “mini1.text” begins here 


x) 

COOK KOKO KKK KKK KK KK RK KKK KK KKK AK KKK KK) 
(x i fi¢¢ Fr © d@wteée«t i oO on *k) 
OK KKK KKK KKK KKK KKK KKK KKK KK KKK KKK KKK KKK) 


FROCEDURE introduction; 
BEGIN 


wipe; (kK clear screen *); 


writeln (*Welcome to miniadventure!’); 


25 


writeln 
writeln 
writeln 
writeln 
writeln 
writeln 
writeln 
writeln 
writelny; 
writeln 
writeln 
writeln 
writeln 
writelns 
writeln 
writeln 
writeln 
writeln 
writeln 
writeln 
writelns 
writeln 


(? Your goal will be to find a treasure’); 
(?and bring it to the starting point.’); 
(7You will also get points for finding’); 
(?each location in the adventure.’ ); 
(*Points will be deducted for various’); 
(7undesirable happenings: waking the’); 
(ogre, getting eaten, getting toasted,’); 
Cfetc.")3 


(*I will guide you and be your eyes and’); 
(ears. I will describe your location’); 
(‘and give you special instructions’); 
("from time to time.*)¢: 


(*To command me, tell me a direction’); 


(*ta take north, south, east,."); 
C(*west, Up, ar down.")s 
(?note: I only look at the first letter")y; 


("of the command, so abbreviations’); 
("are acceptable.’)s 


(* When you are ready to begin your’); 


writeln| (adventure, just press "return". 7)3 


use “write” not “writeln” (writeln pushes top line of text off the screen) 
readln(cammand) ; 


FE.ND 


wipes 


(xk FROCEDURE introduction *)5; 


OOK ROKK KR KKK KK KK KKK KK KKK KKK KK KKK) 


(x 


i n 


i t i a |] i z e x) 


COO OOK KKK KKK KKK KK KKK KKK KKK KK KKK) 


FROCEDURE initialize; 
VAR 


loc: 


BEGIN 


26 


location 
dchars 
done 
quit 
cooked 
eaten 
awake 


rooms; 


starts 
Eq? 4? 
false; 
false; 
false: 
false; 
false; 


readmsqg := false; 
carrying := false; 
trapped := false; 
dropped := false; 
turns = Oy 
twopowEinI]:= 1; 


twopowls]:= 2; 

twopowlel:= 43 

twopowlLwJ:= 8; 

twopowlud: =16; 

twopowld]:=32; 

FOR loc := start TO flames DO 

visitedClac] := false 

(kK enddo *); 


END (x PROCEDURE initialize k); 


OX OK KKK KKK KKK KKK KKK KKK KKK KK KKK KK KKK KKK) 
(x S Cc oO r e x) 
COKKOOK KKK KKK KK RK KK KR KKK KK KKK KKK KKK KKK KK) 


FUNCTION score: INTEGER; 
VAR 

lac: rooms s 

Sc: INTEGERS; 
BEGIN 


tart TO flames DO 


sc r= sc + 10 
(x endif *) 
(kK endda X); 


IF NOT quit 
THEN 


Sc s= sc + BOs; 


IF cooked 
THEN 
sc 


27 


ScCOre := sce 
END (* FUNCTION score X); 


CORR OOK ROKK KK KKK KK ) 
(K congratulatians x) 
CORO OKO KK ROKK KKK KK KOK KK KK) 


PROCEDURE congratulations; 
BEGIN 


IF NOT cooked 
THEN 
REGIN 
IF NOT quit 
THEN 
REGIN 


Capitalize as ‘Congratulations’ or ‘CONGRATULATIONS’ if you like 


wreitelnm (7 XXKKK congratulations XkKKK")3; 


writelns 


writeln (*You got the treasure out in only") 


writeln (turns:4,"* turns. *)3 


END; 


writeln (*You scored’, score:4, * points out’) 


writeln (* of a maximum of 2OO points.")3 


writeln ("Sa lang for now, came again saon!"): 
END 
ELSE 

writeiln (Sorry about that - try again soan'!") 


(kK endif xk); 


readin (command); 
Wipes 


END (x FROCEDURE congratulations *): 


28 


ORK KKK KR KK KKK KR KK KKK KK KKK KKK KKK KKK KKKKK) 
(x w A tft cc AH Ww aiy x) 
CROCK KKK OK KOK OK KOK OK KOK KK) 


FUNCTION whichway:directions; 
BEGIN 


turns = turns + 1; 
REF EAT 
REPEAT 
writelns 
write ("Which way?===%"); 
readin (command) ; 


UNTIL length (command) + O; 


ch := command(eid; 


CASE ch OF 
*n's whichway = 3 
7s" 5 whichway := 3 
"e*s whichway : = @3 
"wk whichway #= wy 
a a whichway #= us 
*d*s whichway = ds 
7 qr quit r= trues 
ENDs 


UNTIL ch IN dcharss 
writelns; 


end (xk function whichway *)43 


COCO OK KKK KKOKKK KK KKK KK XK) 
(x rm oO w a y x) 
COCO OOOO OOK OK ORK KKK KOK OK KOK Ko) 


REGIN 


29 


writelnys 
writeln (Yau cannot go in that directian.*)3; 


END s 

(K(x figs ” 
Bs New file named “mini3.text 
Cte Sees eesesceeceseese ee eseseeeess 26 2.6.8 2) 

Cx Oo g Yr @€@ a c t Gd @ FT x) 


CGO OOOO IC GIGS IGG II KE 


FROCEDURE ogqreaction; 
REGIN 


IF NOT awake 
THEN 
BEGIN 


writein ("This is the ogre’’s lair!’); 
weiteln (If you are not careful, you" "11")3 
writeln ("wake him.")s; 


IF (turns MOD 7)=0 
THEN 
REGIN 
awake = true; 
wreiteln ("Now you’ *ve done it!"); 
writeln ("You woke the agre - better’); 
writeln (get out of here while you can*)3; 


END (xk IF X)5 


END 
ELSE 
REGIN 


writeln ("You wouldn’*t listen ta me would’); 
writeln ("your You really better get out’); 


wreiteln (‘af here before you get eaten! ")s; 


IF carrying 

THEN 
IF (turns MOD 2)=0 
THEN 
BEGIN 


30 


writeln (€* Tao bad! ! The agre caught yau"); 
writeln (and roasted you for dinner.*)s; 
writeln ("Better luck next time!!"); 


eaten := trues 
quit m= trues 


END 
BSE 
REGIN 


wreiteln ("Get cut fast if you dan**t want"); 
wreiteln (*to be a big-mac for the ogre! !"); 


END 
ELSE 
IF (turns MOD S) =o 
THEN 
BREGIN 


weiteln (* Too bad - you’ "ve been eaten! *)s 


eaten : t. 
quit os= true; 


END 
(k endif x) 
END (xk IF NOT awake k), 
END (x FROCEDURE ogreaction ); 
CCK KOKO OK OK OK OK KKK OK KK KKK) 
(x Pp S t a r t x) 
CK ORK OK KKK KKK KK KKK KK KKK KK RK KK KKK KKK KKK) 


PROCEDURE pstarts: 


REGIN 
IF carrying 
THEN ‘ 
done 3: true 
ELSE 
BEGIN 


31 


writelin ("You are standing by a hole in"); 
wreiteln ("the ground. It looks big enough") s 
writeln (*ta climb down."*); 


CASE whichway (GF 


My Sa @aWe noways 
ur writeln ("You can’ *t jump to the clouds!") 5 
ds: location := vestibule; 
END s 
END (xX IF carrying *)5 
END (x FROCEDURE pstart *); 


COCO KOKO KK KOK KOKO KOK OK) 
(x Pp vy e s t i b u 1 e x) 
COOK O OKO K KOK OK OK KKK OK ) 


FROCEDURE pvestibule; 
REGIN 
Use proper capitalization in all of the descriptions throughout this game: 


32 


writeln ("you are in the entrance ta a cave’): 
writeln (*of passageways. there are halls"); 
writeln (‘leading off ta the north, south," ); 
writeln ("and e@ast. above you is a tunnel’); 
writeln ("leading to the surface.*); 
IF dropped 
THEN 
REGIN 
writeln (* Ta the north, through a narrow crack,’ 
writeln ("You can see the treasure. If you?) s 
writeln (stretch your arm through you might*); 
writeln ("reach it. Do you want to try?")s3; 
readin (command) gs 
IF (command = “yes*) OR (command = "y*) 
THEN 
BEGIN 
carrying : = trues; 
dropped := false; 
END (xk IF xX), 


ENDs 
CASE whichway OF 


ne location := nmarrowl; 
Sy locatian := grandraams 
es lacation := iceraoms; 
wed: moways 

us location := starts 


END (x CASE whichway X)5; 


END (x FROCEDURE pvestibule *);5 
OOO ORK OOK KK OOK KOK KKK OK KOK KK ) 


(x p aq roeaondreoaoo i m *) 
COOK OOK OOK OK KK KK ) 


FROCEDURE parandroam; 

REGIN 
writeln (You are in a huge open roam, with"); 
writeln (?an immense expanse of ceiling.*); 
writeln (7A dark passage leads west and a*)3 
writeln (*?narrow crawl leads downward." )3 


CASE whichway OF 


Wi location #= brinks 
d: location := iceroam; 


Tig Sy Gate noways 
END; 
END (xk FROCEDURE pgrandraom *); 
OOOO OK KKK KKK KK KOK KKK KK KK KK KX) 


Cx pon aeerreow fi x) 
COOK OOK KOKO KK KOKORO KK KK KKK KK KK) 


FROCEDURE pnarrowl; 
BEGIN 


writeln ("You are in a narrow passage which") 3 


33 


writeln ("continues to the north. It is"); 
writeln ("extremely narrow to the south.’)3 


writeln (* A very tight crawl also leads east.”*) 
writeln (°A curious odar seeps through it.")4 
writeln (7 I would think twice before trying’)s; 


writeln (*to go that way!*)5 


IF carrying 
THEN 
REGIN 


writeln ("The treasure won"*t fit through”); 


* 
8 


writeln ("the crack going south. Do you want"); 


writeln (*to leave it here?*’); 


readin (command) ; 


IF (command = “*“yes*) OR 
(cammand = “y") 
THEN 
BEGIN 
dropped := trues: 
carrying := false; 


END (kx IF x); 
END (x IF carrying Xk); 
CASE whichway OF 


akeshore; 


mis location 1 
ogreraoms: 


@s location : 
Ss: writeln (? 
Walla ds noways 
END (x CASE whichway *); 
END (x PROCEDURE pnarrowl *) 3 
Ct Peseseses se seseetse ses Fes F353 25393 2 
(x p tl ak es RH oF Ff 6 *) 
COO OOO OR KIO KOK OK OK KKK KK ) 
PROCEDURE plakeshore; 


34 


It*’s too narrow to get through!’* 


) 


® 
a 


BEGIN 


writeln ("You are on the shore of a vast*); 


writeln (‘underground lake. Narrow passages"); 


writeln (’wind away to the east and south. *); 
writeln ("A small island is visible in the’); 
writeln (center of the lake to the north.’); 


CASE whichway OF 


nz: location := island; 
85 location := narrowly; 
@s location := narrow2; 
w.u,ds noaways 


ENDs 
END (x PROCEDURE plakeshore *)3; 
CROOK KKK KK KKK KKK KKK KK KK KX) 


(x p is 1 aon id x) 
CORK OK KKK KKK KKK KKK KKK KKK KKK KK KKK KKK KKK) 


PROCEDURE pisland; 
BEGIN 


writeln ("You are on a small island in the’*)s 


writeln ("center of a huge underground lake.*) 3 
writeln (*Dark frigid waters surround you an"), 
writeln ("’all sides. You can barely make out*) 
writeln ("the shoreline to the south."); 

weiteln (*A small message is scratched in the*) 
writeln ("dirt here. It says: “*The treasure’) 
writeln (?may be found in the maze.**")3 


CASE whichway OF 


My GaWa lia di noways 


Ss 
ENDs 


readmsg 


location := lakeshore; 


:= true; 


END (x FROCEDURE pisland *); 


« 
" 


35 


COOK GOK KK KOK KOK KK KK) 


Cx 


b r i n k x) 


(KKK OK OK OK KOK KKK OK KK KK KK KKK KKK KK KK) 


FROCEDURE pbrinks 
BEGIN 


END 


writeln 
writeln 
writeln 
writeln 
writeln 
writeln 
writeln 
writeln 
writeln 
writeln 


You are on the brink of a steep"); 
incline. The bottom of the pit’); 
is over fifty feet below you. ")s; 
You could probably slide down’); 
(safely, but I won’*t promise you"); 
(that you could get back up.*); 
(* To the west is a dark opening’ 
(into a rubble~filled tunnel. A’ 
("vampire bat just flew out of it’ 
(* shrieking." ) 3 


¢* 
¢* 
(* 
¢* 


? 


yg 
ds 
3 


CASE whichway OF 


MN, Se Gaus 


we 
ds 


END; 


(k PROCEDURE pbrink 


noway 
location := ogreroaom: 
lacation #= pits 

K)s 


CK KKK KKK RK KKK KKK KKK KKK KK KKK KK KKK KK KKK) 


(x 


A 


i 


c oe r o oO Mm x) 


CK KK KK KKK KKK KKK KKK KKK KKK KKK KKK KKK KE KKK) 


FROCEDURE piceroam; 
REGIN 


36 


writeln 
writeln 
writeln 
writeln 
wreiteln 
writeln 
writeln 
writelin 
writeln 


(*You are in a room whose walls are’*)s: 
("formed from a deep blue crystalline”); 
(7ice. To the north a narrow tunnel"): 
Copens. From the other end of the tunnel’); 
Can aminous growling sound may be"); 
(heard. To the east a sparkling *)s 
Cluminescence emanates from a broaad*)s 

(7 apening. To the west a passage"); 

(‘leads back toa the vestibule.” ), 


CASE whichway OF 


es lecation := crystals: 
ms lacation := oagrerocoms 
we location := vestibule; 
Salt,ds noway 3 

END; 


END (x PROCEDURE piceroom *)3; 


COOK KK KOK OK KKK KK KK KK KKK KKK KK KK RK KK KK) 
(x Q g r e r QO Q m x) 
COOK OKO RKOK OK KK KKK KK KK KK KKK KKK KK) 


FROCEDURE pogreroom: 


) 


VAR 
i,j: INTEGERS: 
FROCEDURE generaldescriptions: 
BEGIN 
writeln (* You are in a low room whose walls’); 
writeln (*’are covered with ominous dark’); 
writeln (*gouts of dried blood. The center’); 
writeln (of the room is dominated by a’); 
writeln (*firepit, which contains burned’); 
writeln (“out coals and a lang spit suspend-*) 
wreiteln ("ed aver its center." ); 
writeln ¢? Fram one dark carner emanates a*); 
writeln ("“harrible growling noise like that *) 
writeln (of some unspeakable manster snoring’ 
writeln (during its dream of rending you limb 
wreiteln (fram limb and making you its dinner!’ 
END (xX FROCEDURE generaldescriptian *); 
BEGIN 


generaldescriptioan; 
ogreactions 


IF NOT eaten 
THEN 
BEGIN 


writeln 


(*There are exits to the east, 


west, *) 


) 
) 


37 


New file named “maze1.text” 


writeln C north, and south. 7); 
CASE whichway OF 
we lecatian := narrowls 
es location := batscave; 
mi location := narrow2; 
Ss location := iceraom; 
ds BEGIN 
quit = trues 
eaten := trues; 
writeln (*Oh no!! You dummy!!!" ) 5 
writeln (You just fell in the firepit’): 
writeln (7and made such a ruckus that’); 
wreiteln (you woke the agre. IT hate ta’); 
writeln (*tell you this, but you are"); 
wreiteln ("also trapped!*); 
FOR i = 1 TO 2 DO 
BEGIN 
FOR j s= 1 TO 1900 DO; 
weite d?.7)¢8 
END (xk DO x); 
writeln (*? You have heen added to the"); 
writeiln Coagre**s gourmet recipe library! *)3 
writeln ("Better luck next time.")3 
ENDs 
ue Noway § 
END (* CASE whichway *); 
END (x IF NOT eaten *)35 
END (x PROCEDURE pogreroam ) 4 
Ck CX 
x) 
COOK OK KOK KKK KOK KKK KOK OK ) 
(x a m a = e x) 


COCO KICK KK KOK OK KKK KKK) 


FROCEDURE pmaze; 
TYPE 
Mazerooams = (mi,m2,m3,m4,m5,m6,m7,m8, 


38 


m9,miO,milt,mi2Z,mis,mi4, 
miS,mil6,m17,m18,m19) 


VAR 
mazelacs MAZeraams s 
bitset: ARRAYE directions] OF BOOLEAN; 


FUNCTION bittest 
(ve INTEGER; dir: directions): BOOLEAN; 


BEGIN 
IF (¢(v DIV twopowldird) MOD 2)=1 
THEN 
bittest := true 
ELSE 


bittest := false 
(kX endif *)3 


END (* FUNCTION bittest x); 


FROCEDURE describe(wh: INTEGER); 
VAR 
dirs directions; 


BEGIN 


writeln (You are in a maze of featureless"); 
writeln ("*passaqges. There are exits visible’); 
writeln (“in the following directions:’); 


IF bittest (wh,.n) THEN write (*n 
IF bittest (wh,s) THEN write (*s 
IF bittest (wh,e@) THEN write (’e 
IF bittest (wh,w) THEN write (*w 73 
IF bittest (wh,u) THEN write ("uu 
IF bittest (wh,d) THEN write (*d 


writeln; 


end (* procedure describe *); 


FROCEDURE sameplaces 
BEGIN 


writeln ¢* You have crawled around same*) 
writeln (*twisted tunnels and wound up*) 


“2 Es 


39 


writeln (where you beqan.*)3: 
END (xk PROCEDURE sameplace *)>3 


PROCEDURE treasure; 
REGIN 


IF NOT carrying 
THEN 
REGIN 
IF readmsg 
THEN 
RE(GIN 


writeln (*The treasure is here!!*);5 
writeln (*Do you want to take it now?’); 


readin (command) ; 


IF (command = *yes*) OR 
(command = “y"*) 
THEN 
carrying #= true 


(k endif xk); 
END 
ELSE 
BEGIN 
writein (*The light is extremely dim here’); 
writeln ¢(* You better get out or risk’); 
writeln (*falling into a pit.")3 
END (* IF readmsq *); 
END (* IF NOT carrying ¥)5 
END (X* PROCEDURE treasure *)3 


PROCEDURE pml; 
BEGIN 


writeln (*You are in a maze of featureless"): 


writeln (*passages.")3 
writeln (*from here you can go south, east,*); 


40 


writeln (* west, or up.")3 


CASE whichway OF 


Sy location := ladder; 
@s mazeloc := ms 
we mazelon s= m4; 
us location := steam: 
nads noway s 
END; 
END (x pmil kK); 
FROCEDURE pms 
BEGIN 
describe (nw) s 
CASE whichway OF 
ns mazeloc := mi; 
ws sameplace; 
G2, Satt.ds noways 
END s 
END (x PROCEDURE pm2 x); 
FROCEDURE pind; 
REGIN 
describe(ne)s 
CASE whichway OF 
ns mazeloc := mil; 


e: sameplace; 
SaWalled? NOWAYS 


END; 
END (x FROCEDURE pms *); 


PROCEDURE pm4; 
REGIN 


describe (sew) ; 


CASE whichway OF 


Ss mazeloc := m7;5 
e: mazeloc := m3; 
ws mazeloc := mS; 
Malt, ds noway § 
ENDs 
END (xk FROCEDURE pm4 x); 
FROCEDURE pmSs 
REGIN 
describe(nanly); 
CASE whichway OF 
ns mazeloc := m1; 
BuWy Sally de noway§ 
END: 
END (x FROCEDURE pms *)3 
FROCEDURE pmés 
REGIN 
describe(ne)s; 
CASE whichway OF 
ns mazeloc := m4; 
es sameplace; 


SaWalta ds noway § 
END; 
END (kx PROCEDURE pmé& *)¢; 


PROCEDURE pm73; 
BEGIN 


42 


describe (nsew) : 


CASE whichway OF 


ns mazeloc s= mdz, 
Ss mazeloc := m9; 
e: mazeloc := més 
we mazeloc := m6; 
unde noways 
END; 
END (kx FROCEDURE pm7 k)3; 
PROCEDURE pm&s; 
REGIN 
describe (nw) 3 
CASE whichway OF 
ne mazeloc := mos 
Ws sameplaces 


2,Satt.ds noways 
END: 
END (k FROCEDURE pm x); 


FROCEDURE pm?; 
BEGIN 


describe(sw); 
CASE whichway OF 


Ss: mazeloc +: 
we mazeloc : 


NaG@alta ds noways; 
END; 


END (x FROCEDURE pm? *); 


PROCEDURE pmids 
BEGIN 


describe (ns): 


CASE whichway OF 


ms mazeloc := m8; 
Ss sameplace; 
SaWelladt NOQWAYS 
END; 
END (x PROCEDURE pmiO x), 
FROCEDURE pmii; 
BEGIN 
describe (newud) 3 
CASE whichway OF 
nme mazelac s= m9; 
er mazeloc := més; 
we mazeloc := miOs 
ue mazeloc s= mi; 
d: mazeloc = mizs 
: noways 
END; 
END (kX FROCEDURE pmil *); 
PROCEDURE pmiis 
BEGIN 
describe (dn) s 
CASE whichway OF 
ms mazeloc = mis; 
ds: mazeloc := mié; 


@x,SaWall? MOWAYysS 


44 


END; 
END (XX PROCEDURE pmi2 


FROCEDURE pmi3; 
BEGIN 


describe dn) ; 
CASE whichway OF 


ne mazeloc 
ds mazeloc 


2.S.W,lls maways 
END: 
END (x FROCEDURE pmis 


FROCEDURE pmi4; 
BEGIN 


describe(dn) s 
CASE whichway OF 


ris mazeloc 
ds: mazelac 


Gq SaWa ltt NOWAY & 


END; 
END (* FROCEDURE pmi4 


FROCEDURE pmiSs 
BEGIN 


describe (ud) 5 


CASE whichway OF 


us mazelac 
: mazelac 


NaSaeGaWt NOWays 


ii 
3 3 
ee 
ai “28 


me 


ENDs 
END (* FROCEDURE pmiS *)3 
FROCEDURE pmildés 
BEGIN 
describet(ns) ; 
CASE whichway OF 
ns mazelac := m1l7; 
Ss sameplace; 
@uWalla dt Nowey § 
END; 
END (xk FROCEDURE pmié& &)s 
FROCEDURE pmi7s 
BEGIN 
describei(ns)s; 
CASE whichway OF 
ns mazeloc = mi; 
5: mazeloc := mild; 
@aWatts ds noway 3 
END s 
END (kx PROCEDURE pmi7 *); 


FROCEDURE pmi®s 
REGIN 


describe (ns) ¢ 
CASE whichway OF 


ms Mazeloc 
Ss mazeloc 


I a naoways 


46 


END; 
END (x FROCEDURE pmi8 *); 


FROCEDURE pmi?; 
BEGIN 


describe (su) 
treasure; 


CASE whichway OF 


S mazeloc s= mig; 
ues mazeloc := mids 


Me @aWadi noway § 
END; 
END (x PROCEDURE pmil9 *): 
BEGIN (xk FROCEDURE pmaze *); 
mazeloc := mi; 
REPEAT 


CASE mazeloc OF 


mis pmis 
Mie k pm2y 
mas pmss 
m4 s pm4s 
mos pms; 
Mos pmeas 
m? s pms 
mes pms sy 
ms pm? 3 
miG: pmo; 
mids pmil; 
mics pmizy 
mic3s pmiss 
mids pmi4; 
mis pmics 
mig: pmlé; 
mis pmi7; 


47 


mi8: pmigy; 
miQs pm1l?; 


END (x CASE mazeloc ); 


UNTIL location <=? maze; 


END (k PROCEDURE pmaze *) 3 


(CK OX a ” 
“) New file named “mini2.text 
OX K KK KKK KKK KK KK KKK KK KKK KK KKK KKK KR KKK KEK) 
(x Pp on aA F F @ W 2 x) 


CKO KOK KKK KK KK KK KKK KKK KOK KKK KKK KKK KKK) 


FROCEDURE pnarroaw2; 
BEGIN 


writeln ("You are in a very narrow passage." )3 
writeln (*To the west the passage opens out"); 
writeln ("by a lake shore. Toa the east it is"): 
writeln (even tighter. You just might be"); 
writeln (‘able to squeeze threaugh if you try"): 
writeln ("real hard.*); 

writeln ¢° There is also a strange looking" )s; 
writeln ("alcove in the south wall that seems"); 
writeln (*toa open into a very dark tunnel.*)3; 


CASE whichway OF 


we location := lakeshore; 
e: location := steam; 
= location := ogreraoms: 
Matha: noways 

END; 


END (xX PROCEDURE pnarrow2 *) 4 
COO OOK KK ROKK KK OK KK KKK KK ) 


(x p p i t x) 
COCO OOO OK KKK KKK KOK KOK KK KK KK ) 


PROCEDURE ppit; 
BEGIN 


48 


writeln (You are at the bottom of a fifty"): 
wreiteln ("foot pit. The walls are just a*)3: 
writeln (*hair tao steep ta climb. The pit"): 
wreiteln ("is empty except for a few ald"); 
writeln (dried bones ~ I can**t tell af they’)3 
writeln Care human ar not! ! In the center")s 
writeln ("of the pit is a nmarraw shinny’); 
weiteln (leading downward." ) 3 


CASE whichway OF 
ds: location := ladders 


us BEGIN 
writeln ("If yau try ta climb that, *)3 
writeln (Cyou’"“re sure to kill yourself!")s; 
END; 


Ty Sa @aWe noways 
END; 
END (X PROCEDURE ppit *); 


CK KK KKK KKK KKK KKK KK KKK KR KKK KKK KK KKK KKK) 
(x Pp c r y S$ t ail x) 
COOK OK OK KKK KK KKK KKK KOK KK KKK KKK KKK) 


PROCEDURE perystals 
REGIN 


writeln ("You are in a shining hall af crystal.’); 
writeln ("It is intensely cold but also chill-*)3; 
writeln (ingly beautiful. There are glass’); 
writeln (*formations rising from the floor’)s; 
writeln ("as if they had grown there, yet’); 
writeln ("delicately sculptured with multi-*)4 
writeln ("faceted sides. An intense white’); 
writeln (light shines brilliantly from the’); 
writeln (*floor, which is also made of a"); 
writeln (*mirror smooth lead crystal. The Light’ )s: 
writeln ("is almost blinding and the many"); 
writeln (reflections that it sets off among”): 
writeln ("the crystal formations of the room’); 
writeln (*make it almost impossible to tell’), 


writeln ("where the room begins and where’); 


writeln ("it ends. *)3 


CASE whichway OF 


@s location := maze; 
NaWs lecatian := ogreraam; 


Salles noway § 
END: 
END (x FROCEDURE crystal xk); 
COOK KK KK KKK KKK KK KK KKK KKK KKK) 
(x p b a t S$ © A Vv @ x) 
CK KK KKK KKK KKK KKK KKK KKK KKK KKK KKK KK KKK) 


FROCEDURE pbatscaves;s 
REGIN 


writeln (*You are in a steep cavern filled’); 


writeln ("with shrieking vampire bats. 
writeln (* swoop and dive at you by the 
writeln (* thousands. If I were you, I 
writeln ("get out as quick as I could. 
writeln (*are openings to the west and 


CASE whichway OF 


we location := ogreroom; 
ns location := steam; 
@u Salt, noway § 

END; 


END (x FROCEDURE pbhatscave *)3; 

COOK OOK OKO ROK KK KK KK KKK KKK KK KX) 
(x Pp S t e a m x) 
COOK OK KK KKK KKK KKK KKK KKK KK KK KKK KKK) 


PROCEDURE psteam; 
BEGIN 


50 


They") 5s 

sa 5 
would’) s 

There’); 
north. ")¢ 


END 


writeln 


writeln (’a stifling steamy vapor. There are’); 
writeln ("innumerable small geysers scattered’); 
writeln (about, each contributing its own steam’); 
writeln ("to the general mist.7); 

writeln (*To the west is a dark opening, as"): 
writeln ("well as to the north. Further out’); 
writeln (in the middle of the room is a dark’): 
writeln ("opening in the floor into which weiner 24 
writeln ("af the vapor seems to be seeping’): 


(*You have entered a hall filled with’); 


CASE whichway OF 


we location := narrow2: 
ns location := de adend; 
ds: location := maze; 
Ss location := batscave; 
@ytts noways 

END; 


(kK PROCEDURE psteam *)3; 


CK KK KKK KKK KKK KK KKK KKK KKK RR KKK KKK KKK KKK) 
(x Pp 1 aodqdqeor x) 
COOK GK KKK KKK KKK KKK KK KK KX) 


PROCEDURE pladders 
BEGIN 


writeln ("You are at the base of a huge ladder’); 


writeln (reaching up out of sight. 
writeln (extend up at least SOO feet, 


It must") s 


and it will’); 


writeln (’take someone brave in heart to scale’); 


writeln (it. 
writeln ("lead north and down.’)s: 


CASE whichway OF 


ns lacation := maze; 
d: location := flames: 
us IF carrying 

THEN 

BEGIN 


There are alsa passages which’); 


51 


writeln (*? You can’*"*t carry the treasure up the’); 


writeln ("ladder - it**s much too heavy! "); 
END 
ELSE 


location := vestibule 
(xX endif *)s; 


@,SaWE noaways 

END: 
END (xk PROCEDURE pladder *); 
COOK KKK KKK KKK KKK KKK KKK KKK KK KKK KKK RK KKK) 
(x p f 1 a m e S *) 
CHK KKK KKK KKK KKK KK KKK KKK KKK KKK KKK KK KKK KK) 


PROCEDURE pflames;3 

BEGIN 
weiteln (Unfortunately you have fallen into’)s; 
writeln ("an underground fire pit. It is the"); 
writeln ("source of the heat that produces’); 
writeln (’the geysers in the steam room. You") s 


writeln ("*have been toasted to a crisp to put’); 
writeln ("it politely." )5 


END (x FROCEDURE pflames x); 


CHR KKK KKK KKK KKK KKK KK KK KKK KKK KK KKK KKK KKK) 
(x p d @ a d e n d x) 
CHK KK KKK KKK KKK KK KKK KK KKK KKK KKK KKK KKK KKK) 


PROCEDURE pdeadend; 
BEGIN 


writeln ("Dead end.*); 
CASE whichway OF 
Ss location := steam; 


52 


Na@aWylta cs 


END; 


END 


BEGIN (k === 


noways 


(xk PROCEDURE pdeadend *); 


CHK KKK KKK KKK KK KK KKK KKK KKK KKK) 


cE 


adventure 


{ses 


x) 


CKKK KK KKK KKK KKK KKK KKK KKK KKEKK) 


introductions 
initialize; 


REPEAT 


visitedLlocationg) 


CASE location OF 


starts: 
grandroom: 
vestibule: 
narrowls: 
lakeshore: 
island: 
brinks 
iceroaoms 
agreroom: 
narraws: 
pit: 
crystal: 
batscave: 
steams 
deadend: 
ladders 
MAZE 
flames: 


END (x CASE 


UNTIL quit OR done; 


cangratulations; 


END. 


= true; 


pstarts 
pgrandroom; 
pvestibule; 
pnarrowls 
plakeshoare; 
pisland; 
pbrink; 
piceroom; 
pogreroom; 
pnarraw2; 
ppits 
perystal; 
pbhatscavey; 
psteams; 
pdeadend; 
pladders 
pmaze; 
pflames; 


lacation *); 


53 


Representing the 


In this chapter, I begin to show you how to use the 
Pascal language to write adventure programs. The 
Apple version of UCSD Pascal has been used in all 
the examples in this book. These programs should 
run without major modifications on any system with 
UCSD Pascal. 

Chapter 6 has presented the listing of Adven- 
ture 1 in Pascal. This adventure would not present 
much of a challenge to a seasoned adventurer. In 
fact, even beginners would probably find it boring. 
My purpose was not to write a serious game; I 
wanted to introduce you to some of the features of 
Pascal that are useful in writing adventures. By 
studying these techniques, you can learn to write 
your own adventures in Pascal. I hope that you also 
gain a deeper understanding of Pascal and become a 
better Pascal programmer as a result. 


REPRESENTING THE ADVENTURE MAP 


Figure 7-1 presents most of the initial map of 
Adventure 1. The notation for the map is explained 
in more detail in Chapter 2. Notice the use of the 


54 


Map 


circled question marks and circled numbers to indi- 
cate the problems of the adventure. Figure 7-2 
shows the map of the maze incorporated in Adven- 
ture 1. Finally, Fig. 7-3 lists the problems by 
number and gives explanations of each one. 

There are 18 rooms shown on the map in Fig. 
7-1, not counting all the individual maze locations. I 
will represent these rooms in Pascal by using an 
enumerated type: 


TYPE 


rooms = (start, grandroom, vestibule, narrow1, 
lakeshore, island, brink,iceroom, 
ogreroom,narrow2, pit,crystal, 
batscave,steam,deadend, ladder, 
maze,flames) ; 


VAR 
location: rooms; 


The concept of an enumerated type deserves a bit of 
discussion. 


la 
Ww 
Ss 


deadend 


Fig. 7-1. The map of Adventure 1. 


55 


Fig. 7-2. The map of maze in Adventure 1. 


56 


You must bring the treasure 
to start in order to win. 


To guarantee a win, you must carry 
the treasure to narrow1 drop it, 

go around to the vestibule, and pull 
the treasure through the crack. 

You can get to the vestibule from the 
iceroom, but you must carry the 
treasure through the ogreroom, and 
you could be eaten in the process. 


You cannot carry the treasure up the 
ladder—it’s too heavy! You must find 


another way out while still carrying 
the treasure. 


You must find the treasure deep in the 
maze. Before the treasure will be 
visible to you, you must discover the 
message on the island. 


Fig. 7-3. The problems of Adventure 1. 


ENUMERATED TYPES IN PASCAL 

Programmers sometimes have difficulty com- 
ing to grips with the concept and use of enumerated 
types. The reason for this is a psychological one. A 
large part of traditional programming, whether it be 
in assembly language or in so-called “high-level” 
languages such as FORTRAN or BASIC, has dealt 
with the invention of representations. Programmers 
have become used to this necessity. In Pascal, the 
concept of enumerated type provides a facility 
which removes the need to invent representations 
in certain situations. Suddenly the very program- 
ming language itself solves part of the traditional 
representational problem. The programmer is not 


used to this. The programmer is fixated on solving 
all representational problems—indeed the pro- 
grammer has come to love this aspect of program- 
ming. When faced with a ready-made solution, the 
circuits become overloaded. 

To make this more concrete, suppose for the 
moment that you were writing a miniadventure in 
BASIC. You might use a variable to keep track of 
which room or location the player happens to be in 
at any given point in the game. Now BASIC can on- 
ly manipulate two kinds of data—numbers and 
strings. Therefore, you must invent a correspon- 
dence between the set of rooms in your game and a 
set of numbers. The numbers will be used inter- 


57 


nally to represent the rooms. You might choose 


start = 1 
vestibule = 2 
grandroom = 3 


and so on. 

On the other hand, it would be equally valid to 
use 

start = 1001 

vestibule = 1002 

grandroom = 1003 
and so on. 


If the latter representation were chosen over 
the former, there is a likelihood that the choice 
would be based on some intended programming 
trickery. That is, part of the BASIC program might 
be written to “know” that numbers greater than 
1000 represent rooms. 

In general, unless you resort to coding tricks, 
the specific numbers chosen to represent the rooms 
are purely arbitrary. They only serve to distinguish 
one location from another and at some level, to 
identify which location is under scrutiny. The key 
points about these numbers are: 


@ They are explicity chosen by the programmer. 

@ They represent an external concept (rooms) in an 
internal form (numbers). 

@ They must be correlated mentally by the pro- 
grammer; that is, the details of the representa- 
tion are a factor in the coding of the program. 


Pascal’s enumerated type facility enables you 
to set aside the problem of representation in this 
case. The enumerated type given above effectively 
creates a new kind of data that Pascal can manipu- 
late, auser-defined type called rooms. The variable 
location may be thought of as a variable that can 
take on any roomas its value. The user may think of 
rooms in terms of their names as chosen in the type 
declaration. There is no need to choose a set of 
numbers to correlate to the names: the Pascal com- 
piler takes care of this for you. Since you really 
don’t care what the specific numbers are (unless 


58 


you had planned on some clever trickery), it is 
better to let the details be swept under the rug of 
compilation. When you write your program, you can 
think in terms of room names, not numbers! 

Since long years of training have made most 
programmers feel that we should be personally re- 
sponsible for internal representations, it is difficult 
to make our minds let go of that feeling of responsi- 
bility. However, when we finally do let go, the 
freedom to think in terms of the problem instead of 
in terms of the computer is the result. Once you 
become accustomed to it, this can lead to an enor- 
mous feeling of power and relief. 

If you read through Adventure 1, you will see 
that assignment statements such as: 


location := vestibule; 


location := flames; 


are commonplace. How much more suggestive 
than: 


location := 2; 
location := 17; 


Of course, the enumerated type rooms only 
represents the collection of locations in the adven- 
ture. It does not include information about the con- 
nections between locations. Figure 7-1 includes all 
the connections. This version of the adventure will 
only accept commands that indicate a direction in 
which to travel. In particular, it only recognizes six 
different directions represented by the single let- 
ters n, s, e, w, u, and d. 

From any given location only certain direc- 
tions lead to other locations. For example, from the 
Crystal room you may only proceed e, w, or n. 
There is no way to go u, d, or s. This information 
must be encoded in some fashion and interpreted by 
the adventure program in order for the game to be 
played. This has been accomplished by two 
techniques that I explain in the next chapter. In the 
process, I shall reveal how the general play of the 
game is controlled. Both the representation of the 
adventure map and the control of the game hinge on 
the use of the Pascal case statement. 


Controlling the Play 


In the last chapter, I discussed enumerated types in 
Pascal. The power of enumerated types is greatly 
enhanced by the use of the Pascal case statement. 
There are several examples of case statements in 
Adventure 1. They all are quite important in making 
the game work. I am devoting this chapter to a 
discussion of some of these statements. 


CASE STATEMENTS IN PASCAL 


The general form of a Pascal case statement 
may be indicated as follows: 


case E of 


I1: 
l2: 


al; 
a2; 


In this general description, there are a number 
of elements that require further explanation. 


case expression E 


E must be an expression that evaluates to one 
of the elements of what in Pascal is referred to as a 
scalar type. In particular, enumerated types are 
examples of scalar types. In the context of adven- 
ture games, E could be a variable representing a 
value of type rooms. Or, it could be a call to a 
function that returns a value from the type direc- 
tions= (n,e,s,w,u,d); More on this below. 

The idea of the case statement is to allow a 
program to take different actions based on the par- 
ticular values of an enumerated type. The elements 
11,12, ...,ln of the general case statement repre- 
sent the possible values of the enumerated type. 
Actually each li may be a list of elements from an 
enumerated type. The elements al,a2, ... ,an in 
the general description represent the Pascal code 
that is to be activated in response to the corre- 
sponding value or list of values li. 


59 


In Adventure 1 there are two major uses of the 
case statement. Both involve the flow of control 
through the game program. 


CONTROLLING THE ADVENTURE GAME 


If adventure were real, the player would either 
be in a location at a given time, or he would be 
traveling between two locations. In the act of 
traveling the player would leave one location for 
another by heading off in a specific direction, such 
as north, east, or down. Thus, in programming 
adventure these ideas must be represented in some 
way using Pascal code. Specifically, you must find 
ways to represent: 


@ The adventurer’s location. 

@ The possible directions out of each location and 
the destinations they represent. 

@ The decision as to which direction to take. 

m The changing of location from one room to 
another. 


The Adventurer’s Location 


I have already discussed the use of an enumer- 
ated type (which I called rooms) to represent the 
possible locations. To store a specific value of the 
rooms type, I invented a variable called location. 


var 


location: rooms; 


At the beginning of the game, the location 
variable is assigned the value start. This represents 
the initial room or starting point of the adventurer. 
At various places in the adventure game code, as- 
signments will be made to this variable. Such as- 
signments will be dictated by the adventurer’s 
choice of direction. 

To organize the game, a separate Pascal pro- 
cedure has been written for each value of the rooms 
type. These procedures have all been given names 
that consist of the letter p for procedure, followed 
by an identifier from the rooms type. For exam- 
ple, there are pstart, pflames, pgrandroom, 
pogreroom, and so on. Each of these procedures 
handles the activities that may take place in the 


60 


corresponding location 
The main program code consists largely of a 
case statement with case expression of type rooms: 


case location of 


start: pstart; 
grandroom: pgrandroom; 
vestible: pvestibule; 
narrow1: pnarrow1; 
lakeshore: plakeshore; 
island: pisland; 

end; 


The meaning of this statement is extremely 
simple: When the player is in room x, activate the 
procedure px, which handles activities in that 
room. At any time during the game, one of these 
procedures will be in execution. The execution of 
that procedure corresponds to the adventurer actu- 
ally being in that room in real life. The program’s 
structure very closely reflects the way we imagine 
a real life adventure would happen. 

Look back at the main program listing in 
Chapter 6 and notice that the case statement is 
enclosed within a while statement. This is neces- 
sary in order to ensure that the case statement is 
repeated over and over again. Otherwise, the game 
would cease after pstart was executed once. 

The use of a case statement nested inside a 
while statement is common in Pascal programming. 
Remember it—you will probably use it often during 
your Pascal programming career. 


Changing Locations 


Part of the activity in each room is deciding 
which way to go next. The adventure map shows 
that in a given location it is impossible to proceed in 
all possible directions. Only a subset of the possible 
directions represent actual paths in the imaginary 
adventure world. The code must reflect this fact. In 
Adventure 1, each location procedure contains 
another case statement that handles the travel from 
location to location. The case statement also en- 
codes the possible directions that may be taken. 
This is best explained by considering an example: 


The following code is found in procedure 
plakeshore: 


case whichway of 
n: location := island; 
s: location := narrow1; 
e: location := narrow2; 


w,u,d: noway; 


end; 


The identifier whichway is the name of a function 
that determines the direction the adventurer 
wishes to take. This is described in more detail 
below. The identifier noway is the name of a proce- 
dure that simply prints the message: 


“There is no way to go in that direction.” 


on the game player’s display screen. 
The operation of the case statement is once 
again quite simple. 


@ A direction is chosen by the adventurer. This is 
accomplished by a call to whichway. 

wlf the direction chosen represents a possible 
avenue of travel, the new room is assigned to the 
variable location in the appropriate section of 
the case statement. This will cause a new loca- 
tion procedure to be invoked on the next cycle of 
the main case statement. 

g If the direction chosen does not represent a pos- 
sible route in the adventure map, the noway 
procedure is called. The value of the variable 
location does not change. This means that the 
same location procedure will be invoked on the 
next cycle. The adventurer gets another chance 
to find a way out. 


Deciding on a Direction 


In this adventure, only very simple commands 
are used; only one of the six directions Nn, S, e, W, U, 
or d may be specified. The function whichway is 
responsible for this interaction with the player. It 
prompts by “asking” 


Which way?===> 


The player responds by keying in a “command.” 
This may be any string of characters. The Pascal 
code examines only the first character of the string: 


command{1] 


If this character is one of the letters n, S, e, W, U, or 
d, whichway sets its return value to the corre- 
sponding value from the enumerated type direction. 
For example, if the adventurer types in never, 
whichway will return n as the direction to try. 


Protecting Against Empty Responses 


There is one problem that the Pascal code in 
whichway must solve. If the adventurer simply 
pushes the RETURN key the variable command, 
which is used to store the response, will contain an 
empty string. The length of the empty string is, of 
course, 0. The character to be assigned to ch using 
command{1] is then nonexistent. If a statement in 
the program attempts to access this character, the 
Pascal run time system will complain with a cryptic 
message. The adventurer will be yanked from 
pleasant fantasy back to mundane computer reality. 
You must avoid this if at all possible, and it turns out 
to be quite simple to do so. The program simply 
checks the length of command and refuses to touch 
it until the length is greater than 0. 


Changing Location 


The act of changing location is represented by 
a simple assignment statement giving a new value 
to the variable location. If you now go back to the 
listing again, you will find that such assignments are 
scattered all over the case statements that begin 
with 


case whichway of 


These assignments collectively determine the map 
of the adventure. That is, the connections between 
different locations is implicit in these assignments. 

This last point is worth a minute’s reflection. If 
you were writing adventure games in a language 


61 


like BASIC, you would probably encode the map as 
an array of numbers. The array would probably have 
two dimensions, with any pair of subscripts repre- 
senting a pair of locations. The contents of the array 
for each such pair would be yet another number. 
This number would be a code representing one of 
two things: 


1. The direction to take to get to the room rep- 
resented by the second subscript from the room 
represented by the first subscript. 


or 


2. A number that means there is no connection 
between the pair of locations in question. 


In terms of Adventure 1, you could imagine an 


62 


array MAP[18,18]. The rooms start, grandroom, 
vestibule, narrow1, and so on would be rep- 
resented by the numbers 1 to 18. Then the pair of 
subscripts 1,3 would represent start, vestibule; the 
pair of subscripts 4,17 would represent narrow1, 
flames; the pair of subscripts 5,9 would represent 
lakeshore; ogreroom; and the pair of subscripts 
5,6 would represent lakeshore, island. 

The directions n,s,e,w,u,d, and noway might 
be represented by the numbers 1,2,3,4,5,6, and—1. 
Then MAP[1,3] would equal 6; MAP[4,17] would 
equal—1; MAP[5,9] would equal—1; and MAP[5,6] 
would equal 1. 

I could have taken an approach like this in 
Adventure 1. However, it was unnecessary to do 
so, because the Pascal implementation I have cho- 
sen was available and was so much easier to under- 
stand and work with. 


Mazes in the Middle 


Adventure 1 has a maze in it. The original adven- 
ture game had not one but two mazes in it. Many 
subsequent adventures, especially those of the un- 
derground or cave variety, also contain mazes. 

The maze offers a good opportunity for intro- 
ducing more of the structural features of Pascal. 
The maze is conceptually like a single location. Yet 
in practice it contains many individual locations. 
While you are in the maze you are “trapped” in a 
miniature adventure within an adventure. 

The entire adventure in Pascal is implemented 
by a program. The maze is implemented by a single 
procedure, pmaze. By analogy, because the maze 
is like a miniature adventure, pmaze must be like a 
miniature program. Indeed as you shall see, proce- 
dures can have almost all of the features that a 
Pascal program can have. 

Although pmaze is a single procedure that 
controls play in the maze, it has its own local or 
nested procedures and a nested function. Local pro- 
cedures and functions are just like the procedures 
and functions that contain them, as far as Pascal 
syntax rules are concerned. However, they may 


only be invoked from the procedure or function that 
contains them or from other functions and proce- 
dures that are also local in the same sense. 

The pmaze procedure also contains a local 
TYPE and VAR section. The types and variables 
thus declared may only be used inside pmaze. They 
are not “visible” outside pmaze; pmaze is referred 
to as the scope of these declarations. The scope 
includes any nested procedures or functions that do 
not redeclare the types or variables in question. 

The concept of scope is fundamental to Pascal 
and other languages like it, which are referred to as 
block structured languages. It is beyond the scope 
(pun intended) of this chapter to go into a full dis- 
cussion. For full detail on this subject, consult your 
authoritative Pascal language textbook. However, 
by imitating these program layouts you should not 
run afoul of the scope rules. 

Local or nested declarations, including proce- 
dures and functions, are provided in the language to 
allow for better program structure. If a variable 
logically belongs only to a certain procedure, it is 
best to declare it there and there only. Then it 


63 


cannot interfere with other variables that have been 
declared in the outermost part of the program. A 
good example of this (in general) is a loop control 
variable for a counting loop. Such a variable should 
be a VAR declared locally within the procedure or 
function that uses it. In some implementations 
UCSD Pascal allows you to violate this guideline. 
The International Standards Organization (ISO) 
standard for Pascal does not. 

Local procedures and functions are useful for 
breaking down the code of a large procedure or 
function. Good program design requires that each 
procedure perform a single logical purpose. The 
name of the procedure should suggest that purpose 
inasimple way. pmaze is a good example, although 
the name is somewhat cryptic. It would have been 
more understandable if I could have called it some- 
thing like handle__the__play__while__the 
__adventurer__is__in__the__maze, but UCSD 
Pascal limits the significance of an identifier to its 
first eight characters. So if I ever named another 
procedure with a long name beginning with 
handle__the play — while __ the __ player 
_is__in_. .. , I would have a name conflict. In any 
case, the procedure pmaze handles a single pur- 
pose that is easy to state and to comprehend. 
Nevertheless, it is useful to be able to write proce- 
dures and functions that are subordinate to pmaze 
and that help pmaze carry out parts of its task. I 
shall discuss these subordinate functions and pro- 
cedures later. 


LOCAL DECLARATIONS 


The procedure pmaze declares an enumer- 
ated type called mazerooms. The identifiers in this 
type represent the individual maze locations. It is 
used in a fashion analogous to the rooms enumer- 
ated type in the main program. The maze rooms 
are not given individual names, nor are they given 
individual descriptions. Thus, I just call them m1, 
m2, m3, and so on. 

There is a single local variable declared in 
pmaze—mazeloc. It is of the type mazerooms. 
Its value represents the current location of the 
player in the maze. The value is always one of the 
elements of the local enumerated type maze- 


64 


rooms. The variable mazeloc is entirely analogous 
to the global variable location. 

I could have implemented the maze by simply 
extending the global type rooms to include the 
maze locations and using the variable location to 
keep track of them as well as of the other locations. 
In fact, this is what I do in the next adventure later 
on. For now, I have chosen the local approach to 
illustrate a point about Pascal. I shall let you decide 
which implementation of the maze you like better 
when you have seen both. 


LOCAL PROCEDURES AND FUNCTIONS 


The pmaze procedure has a large number of 
local procedures and a local function. The proce- 
dures fall into two classes—“location” procedures 
and “support” procedures. The support procedures 
carry out general tasks. They may be invoked from 
several places within the overall pmaze code, or as 
in the case of treasure, they may simply serve to 
make the program more understandable. 

The support procedures are describe, same- 
place, and treasure. They perform functions that 
are suggested by their names. The describe proce- 
dure is used to print the general description dis- 
played when the player visits any of the maze loca- 
tions. The sameplace procedure is used when a 
move by the player results in landing in the same 
maze location as before the move was made. The 
treasure procedure handles the discovery of the 
treasure that is located in one of the maze rooms. In 
addition, describe tells the player in which direc- 
tions it is possible to continue from any given maze 
location. This makes it easier to map the maze. 

The location procedures of pmaze are pm1 
pm2, . . ..pm19. Each of these procedures corre- 
sponds to one maze location. They are analogous to 
the location procedures of the entire adventure 
(pmaze itself is one of these). However, pm1 
through pm19 handle only the maze locations, 
which are sublocations within the maze. All of these 
procedures perform the following duties: 


gw Print a description by calling describe. 
w@ Determine the direction of travel desired by the 
player, by calling whichway. 


@ Determine the resulting maze location or adven- 
ture location after the player’s move is carried 
out. This is accomplished in each procedure by 
using a case statement as discussed in the previ- 
ous chapter. 


The location procedure pm1 does a little ex- 
tra. In response to a player’s directions, it allows 
not only mazeloc but also location to change. This 
is the way the player gets out of the maze—by 


returning to pm1 and then giving a direction that 
goes out of the maze. 

The only local function in pmaze is bittest. It is 
an example of poor Pascal coding practice. It is 
included because I wrote it that way originally, and 
because it illustrates the kind of thinking habits that 
FORTRAN and assembly-language programmers 
fall into. It uses so-called “bit-fiddling” techniques 
that are better represented in Pascal using set vari- 
ables. 


65 


Other Techniques Used in Adventure 1 


In this book, I make every attempt to implement the 
adventures using “general” code. That is I want 
procedures and functions to serve as many parts of 
the adventure as possible. I also want to write 
procedures and functions that may be used as pat- 
terns. Some, like whichway, can be used directly in 
other adventures. Others like the location proce- 
dures provide a skeleton or template. The same gen- 
eral approach may be used in other adventures to 
accomplish the same goals. 

There are many times, however, when you 
just have to give up on generality. There are cases 
in which you need fairly elaborate code in order to 
implement some feature of the game, and that code 
cannot be used for anything but that single feature. 
There are examples of this kind of code in Adven- 
ture 1. 

The location procedure pstart, pvestibule, 
pnarrow1, pisland, pogreroom, ppit, pladder, and 
pflames each contain one or more sections of spe- 
cial code. Let’s just go through them to get a flavor 
of the kind of things that are possible. 


66 


The pisland Procedure 


This procedure provides the simplest possible 
example. The procedure pisland contains the 
statement 


readmsg: = TRUE; 


What this represents in the game is the fact that the 
adventurer has read the message to be found on the 
island. This is important later on—in the maze— 
because the treasure cannot be found until the mes- 
sage has been read. This is an extremely simple 
example of solving a problem. In this case, the 
adventurer solves the problem just by visiting the 
island. Later, when you add commands to your 
adventures, you might require the adventurer to 
explicitly command the guide to READ MESSAGE 
or something like that. 

There is a general principle in this example, as 
well. The variable readmsg is of the Boolean type. 
This means that its value may either be true or 
false. Such variables are useful for representing 


events in adventure games. More precisely, a 
Boolean variable may be used to record the occur- 
rence of an event. In this example, the event was 
the reading of the message. As long as readmsg has 
the value false, the event has not occured. When 
and if the value becomes true, the event has oc- 
curred. 


The pflames Procedure 


This procedure is similar to the pisland proce- 
dure. It sets two Boolean variables, namely, 
cooked and done to true. This effectively says that 
the adventurer has been cooked and that the ad- 
venture is over. Not much more to say, is there? 


The pstart and ppit Procedures 


Both of these procedures have slightly dif- 
ferent case statements than the other location pro- 
cedures in Adventure 1. In each, there is one direc- 
tion that causes a special message to be printed. 
This happens rather than either the location being 
changed or the noway message being printed. In 
each case, the difference is not important to the way 
the game is played or to the result. The only pur- 
pose for the extra messages is to add interest. 

In ppit, ifthe player tries to go u, the message: 


Try to climb that, and you'll kill yourself. 


appears. This message is consistent with the ear- 
lier description in location brink. There, the player 
is warned that sliding down into the pit is possible, 
but climbing back out doesn’t seem possible. The 
ppit special message confirms this message. 


The pvestibule, pnarrow1, 
pladder, and pstart Procedures 


In each of these procedures, one or more 
Boolean variables is examined in order to detect 
conditions that require special action. In each case, 
further setting of Boolean variables may take place 
depending on what happens. 

In pladder, the variable carrying is ex- 
amined. If carrying is true, it indicates that the 
adventurer has located the treasure and is carrying 
it. In this case, it is impossible for him to climb the 


ladder, because the treasure is too heavy. Other- 
wise, going up the ladder is allowed. 

In pnarrow1, the variable carrying is ex- 
amined again. If it is true here, the guide tells the 
adventurer that going south with the treasure is not 
feasible, because the crack in that direction is too 
narrow. The adventurer is then given a chance to 
drop the treasure. If the answer is yes, Carrying is 
set to false and the variable dropped is set to true. 
The latter variable is used in pvestibule later on. 

In pvestibule, if dropped is true, the treasure 
is sitting abandoned in location narrow1. The guide 
tells the adventurer this and allows the treasure to 
be reached for through the crack. If the adventurer 
agrees to try to reach for it (can you imagine anyone 
who would not agree to do that?), carrying is set 
back to true and dropped is set back to false. This 
allows the adventurer to win the game by then 
returning to location start. 

In pstart, the variable carrying is looked at. If 
it is true, the variable done is set to true, which 
means that the game is over. The fact that carrying 
is true when the game ends means that the adven- 
turer has succeeded in finding the treasure and 
getting it back to the starting location. In short, the 
adventurer has won. 


The pogreroom Procedure 


This location contains the most elaborate spe- 
cial case code of any location. In fact, there is a 
subsidiary procedure, ogreaction, which is in- 
voked to handle a lot of the action. 

The procedures pogreroom and ogreaction 
deal with the adventurer’s interactions with the 
ogre. There are only bad experiences to be had in 
the ogreroom! The Boolean variables awake, and 
eaten control these. If awake is true, you risk 
being eaten by the ogre. Your chances are 1 to 5 if 
you are not carrying the treasure, but 1 in 2 if you 
are carrying it! If the ogre is not yet awake, each 
time you visit his lair, you stand a 1 in 7 chance of 
waking him. 

If you should be silly enough to ask to go in 
direction d while you are in the ogreroom, you will 
also be eaten. This corresponds to walking directly 
into the ogre’s fire-pit. 


67 


Preview of Adventure 2 


The second example of a Pascal adventure game is 
based on the first. The map has been extended 
considerably, and many additional capabilities have 
been added. This is the first “real” adventure game, 
because it allows the player to issue commands and 
control the play much more directly than the first 
game did. In many ways, Adventure 2 is the heart of 
the book. I shall continue to discuss it in one way or 
another up until Chapter 25. 


MAPS, DIAGRAMS, AND CODE OUTLINES 


Figures 11-1 through 11-9 present the code 
outlines, structure diagram, and maps of Adventure 
2. Because Adventure 2 is an extension and modifi- 
cation of Adventure 1, you should compare Figs. 
11-1 through 11-9 to Figs. 5-1 through 5-8. The map 
of Adventure 2 is similar to that of Adventure 1, 
with some extensions. Figure 11-7 shows the dif- 
ferences between the map of Adventure 1, which 
was presented in Figs. 7-1 and 7-2, and the map of 
Adventure 2. Figure 11-8 presents the map of the 
additions to Adventure 1. 


68 


CHAPTER PREVIEWS 


Chapter 13 is called “Command Processing in 
Adventure 2” and discusses the use of enumerated 
types in representing commands. It shows how the 
cmdlookup function uses a linear search with a 
sentinel to determine which command the player 
has entered. In the process, it deals with arrays 
indexed by an enumerated type, the input of values 
of an enumerated type, the Pascal succ function, 
and other related topics. 

Chapter 14 is called “Carry and Drop: Pascal 
Sets.” It deals with the use of the Pascal set types to 
represent collections of objects in the adventure 
game. In particular, it shows how the player’s 
“stash,” that is, the objects being carried at a given 
time, is represented by a Pascal set. It also shows 
how the collection of items at each location is rep- 
resented in a similar fashion. A comparison of the 
use of Pascal sets with the implementation of sets in 
ad hoc BASIC code presented. Finally, the use of 
the set operators in Pascal is discussed in the 
course of presenting the implementation of the 


PROGRAM miniadventure; 


CONST, TYPE, and VAR declarations: 


This section of the program contains 
the declarations of the constants, 
types, and variables used by the 
rest of the program. 

See Figure 11-2 


procedures and functions 


This section contains the headers and 
code for the procedures and functions 


accessible to the main 
program block and the outermost 
level of the program. 


See Figures 11-3 and 11-4 


The main program block of Adventure 2. 


See Figure 11-5 


carry and drop commands in the adventure game. 

Chapter 16 is entitled “Problems in Adventure 
2.” Problems, as I have emphasized, are the soul of 
adventure games. The techniques for representing 
problems and their solutions in Pascal code are 
therefore paramount. This chapter deals with the 
use of Boolean variables and Boolean expressions 
in the problem-solving process. It discusses the 
particular problems posed by Adventure 2 and both 
their external face, the way the player views them, 


Fig. 11-1. The code outline for Adventure 2: the 
program outline. 


and their internal representation, the way they are 
implemented. 

The Pascal set membership operator, IN, and 
its role in Boolean expressions is discussed. Other 
elements of Boolean expressions are explained in- 
cluding numeric relationships, and the AND, OR, 
and NOT operators. 

Chapter 16 deals with the ad hoc techniques of 
Adventure 2. It is called “Other Techniques Used in 
Adventure 2.” The Pascal coding solutions for 


69 


rooms 
directions 
cmds 


} As in Adventure 1 


represents the commands the user may issue in 


Adventure 2 apart from directions 
objects 

represents the objects found in Adventure 2 
collection 

represents any subset of objects 
placerec 

a record type describing entries in the index 


part of the descriptions database 


xfile 
narrate 
file variables corresponding to the descriptions 
database index and text data files, respectively 
places: ARRAY [rooms] of placerec; 
an array to hold a copy of the descriptions 
database index in memory 
whatshere: ARRAY [rooms] of collection; 
an array corresponding to the set of objects 
present at each location—modified by the carry 
and drop commands. 
stash 
represents the objects carried by the player 
cmdnames, objnames 


Fig. 11-2. The code outline for Adventure 2: 
important data declarations. 


arrays of strings used to match command names 


and object names typed by the player 
hasdug 
counts the number of times the player has dug 
done, quit, carrying, dropped, chgloc, etc. 
variables to track the occurrence of various 
events during game play 


counting the number of turns the player has taken, 
displaying the contents of a set, scoring the game, 
dealing with the lamp, and implementing the eat and 
dig commands are explained. The handling of the 
ogre character in the game is discussed. The 


70 


simplification of the process of changing locations is 
explained, in particular, the function of the new 
procedure travel. 

Adventure 2 uses files for some of its informa- 
tion. This is hinted at in Chapter 16. However, the 


information in files is a complete data base of de- detail in the next section of the book starting with 


scriptions of all the locations in the adventure Chapter 18. 


game. How this database is created and all the Chapter 17 is a collection of helpful hints re- 
techniques associated with it are dealt within great garding the use of the UCSD Pascal system. It is 


PROCEDURE introduction; 
PROCEDURE initialize; 


} As in Adventure 1 


PROCEDURE showobjects; 
Tells the player what objects are present, if any, at the location 
currently being visited. 
PROCEDURE show; 
Prints the full description of the location just entered. 


FUNCTION score: INTEGER; } As in Adventure 1 
FUNCTION objlookup: objects; 


Determines which object name, if any, a user-typed string matches; 
returns the internal value of said object. 


FUNCTION ckobject (it: objects) : BOOLEAN; 
Determines if a given object is present in the current location; 
returns true if it is; false, otherwise. 
PROCEDURE pcarry; 
PROCEDURE pdrop; 
PROCEDURE phelp; 
PROCEDURE plight; Procedures used to 
PROCEDURE pinventory support the execution 
PROCEDURE ppush; of specific commands 
PROCEDURE pdig; in Adventure 2 
PROCEDURE popen; 
PROCEDURE plook; 
PROCEDURE peat; 


FUNCTION cmdlookup:cmds; 
Similar to objlookup, but determines commands rather than objects. 


PROCEDURE listen; 

FUNCTION docommand:CHAR; 

FUNCTION whichway: directions; 

PROCEDURE noway; 

PROCEDURE travel (nloc,sloc,eloc,wloc,uloc,dloc:rooins); 


Procedures and functions for general command handling and 
changing player's location from one “room” to another. 


PROCEDURE cklamp; 
Monitors the lamp; warns player about staying in the dark, and so on. 


Fig. 11-3. Code outline for Adventure 2: the procedures and functions: Part |. 


71 


PROCEDURE oreaction; 
PROCEDURE pstart; 
PROCEDURE pvestibule; 
PROCEDURE pnarrow1; 
PROCEDURE pisland; 
PROCEDURE pogreroom; 
PROCEDURE ppit; 
PROCEDURE pladder; 
PROCEDURE pflames; 


Location procedures 

as in Adventure 1. Because 
travel is itself a 

procedure in Adventure 2, 
many locations no longer 
need a location procedure. 
(See Figure 11-5) 


PROCEDURE pmaze; Location procedure for the maze. 
PROCEDURE describe; Local procedures as in 
PROCEDURE sameplace; Adventure 1. 
PROCEDURE travel (nloc,sloc,eloc,wloc,uloc,dloc:rooms); 

Local proceure—handles travel within the maze. 


PROCEDURE pm1; 
Local procedure—handles travel within the maze. 


BEGIN 


REPEAT 
CASE location OF 
maze,m1: pm1; 
m2: travel (m1,same,m0,m0,m0,m0); 


m19: travel (m0,m18,m0,m0,m15,m0); 
END; 
UNTIL (location<maze) or (location-flames); 


END(+ PROCEDURE pmaze *); 


Function block for pmaze handles travel inside the maze. 
AREPEAT statement invokes the local travel 
procedure until the player gets out of the maze. 


Fig. 11-4. The code outline for Adventure 2: the procedures and functions: Part Il. 


entitled “UCSD Pascal Development i1echniques.” your files, entering them into the system using the 
There are discussions of how to manage your files UCSD Editor, splitting a large program into multi- 
effectively and UCSD tricks and pitfalls. Organizing ple files, and using the include option in the com- 


72 


BEGIN 


introduction; 
initialize; 


} As in Adventure 1 


REPEAT 
visited [location] := TRUE; 
show (location); 
cklamp; 


Perform general processing associated with changing locations. 


CASE location OF 

start: pstart; 

grandroom: travel (nowhere, nowhere, nowhere, 
brink, nowhere, stairs); 

vestibule: pvestibule; 

narrow1: pnarrow1; 

lakeshore: travel (island, narrow1, narrow2, 
nowhere, nowhere, nowhere); 


flames:pflames; 
END; 
UNTIL quit OR done; 


Main control loop of Adventure 2. If a visit is made to a 
location for which no special handling is required, 

the travel procedure is invoked to process commands 

and move to a new location. Other locations, for 

example maze and narrow! still require location procedures. 
These location procedures handle special conditions 

as well as invoke travel. 


congratulations; } As in Adventure 1 


Fig. 11-5. The code outline for Adventure 2: the main program block. 


piler are all touched on in the discussion. Acouple the UCSD system. Finally, a general discussion of 
of filer tricks involving the prefix command and the _ pitfalls in software development under the UCSD 
K(runch command are introduced—these trickscan system is presented. 

save you time and trouble in your day to day use of 


73 


congratulations 
introduction 


cmdlookup 


objlookup ckobject 


Fig. 11-6. Structure diagram for Adventure 2. 


74 


lakeshore 


deadend 


Sara’ 


Fig. 11-7. Map of Adventure 2: Page 1. 


75 


2 
= 
fe} 
° 
> 
® 
c 
fe} 

£ 


Fig. 11-8. Map of Adventure 2: Page 2. 


76 


Fig. 11-9. Problems of Adventure 2. 


You must carry the lamp and light it in order not to fall into a pit. 
You must bring the treasure to start in order to win the game. 


The treasure in Adventure 2 is locked in achest. You must find and 
carrv the key—otherwise, youcannot open the chest when you 
dig it up. 


You must visit the island and read the message before you can 
dig up the treasure. 


The treasure in Adventure 2 is buried. In order to be able to dig for it, 
you must locate and carry the shovel. When you dig for the 
treasure, you must dig three times to fully reveal the chest. 


You must drop the treasure at narrow1 and push it through the 
crack leading to the vestible. There is no other way to get it out. 


You cannot carry the treasure up the ladder—it is too heavy. 
You must discover the trick of Problem 5. 


77 


Adventure 2’s description database file 
can be found in Appendix B, page 286. 


PROGRAM 
CONST 

f e 
TYPE 


roams 


Pascal Adventure 2 


miniadventures 


(start, grandroam, 


lakeshore, 
ogreroam, 
hatscave, 
Maze, stai 


incline, honeycomb, 
mudroam, deeppool, 
rockyroam, 


vestibule, narrowl, 


island, brink, 
narrow2, pit, crystal, 


Steams 


rs, echoes, 


narroaow4, river, 


alcove, #1 


AMES « 


Mo, Mo, m7, MB, 
mis, milo, mi?, 
same, moawhere®) 


mis, mi4, 


directions = (Ny 


78 


Sy @, 


deadend, 


mo, mid, 


Wey Uy Gd): 


iceraam, 


ladder, 


WAIT TH CCIM 9 
reundroam, 
coldroom, narrows, 


siltroam, 


mo, ml, me, m3, m4. 
mii, mis, 


19, m19, 


cmds = (carry, drop, help, light, 
invent, take, tally, push, 
dig, look, apen, unlock, 
eat, moacmd) s 


objects = (lamp, treasure, keys 
sandwich, bottle, shovel, 
nook j)s 

collectian = GET OF abjects; 

pname = §STRINGL40]; 

storyline = STRINGCS8OI; 

byte = O.,.2553 


whichsect = (indexsectian, descsection); 


“whichsect” is changed to “whichsection” here; this doesn’t matter 
to Apple Pascal, but may cause problems with other versions of Pascal 


placerec 


RECORD 


CASE section Jwhichsection | OF 


indexsections ( tableentry: INTEGER) ¢ 


deecsectian: { names pnames 
id: INTEGER; 
dbegins INTEGERS 
dends INTEGER s 
links byte 3 
END s 
VAR 

xfiles FILE OF placerecs; 

narrates FILE OF storylines 

places: ARRAYCraoms] OF placerecs 

whatshere: ARRAYC rooms] OF collections 

visited: ARRAY Draams] QF BOOLEAN; 

stash: collectian; 

cammands: STRING; 


79 


80 


{ holds user typed directian 


head, 
tails 


{ hald SEFARATE words OF command 


cmdnamess 
ob jJmamess 
che 


dcharss 


{ characters which correspond TO the 
acceptable initial 
directiean 


C*n*, 


lacatioan: 
ogreloc: 


speciall:s 
nexts 
turns: 

is 


indarks 
hasdug 


dane: 
quit: 
lits 
eaten: 
awake: 
readmsqs 
carrying: 


STRING; 


ARRAY Cemds] OF STRING; 


% 
4 


%, 


a 


ARRAY Cob jects] OF STRING; 


CHAR, 


SET OF CHAR; 


commands. 

"ery Tw, TU, 
rooms s 

rooms: 

SET OF roams; 


directions; 


INTEGER; 
INTEGER; 
INTEGER; 
INTEGER: 


BOOLEAN; 
ROOLEAN; 
BOOLEAN; 
BOOLEAN; 
BOOLEAN: 
BOOLEAN; 
BOOLEAN: 


dropped: BOOLEAN: 
trapped: BOOLEAN s 
coakeds EQOLEAN s 
candig: BOOLEAN § 
chagloc: ROOLEAN: 

CORO OK OK KF 

t p @e } 

OOK KOKO KK KD: 


PROCEDURE wi pes 


BEGIN 


letters OF 
initialized 


7q°J 


TO 


“a 


write (chr (ff) s 
END § 


{(SiaZ.ud,text? 

($iaze,~us. text} 

($ial.me2. text? 

{fiad,.maze. text} 

(Fia2.main,. text} 

{ source FILE: aZ.ul.text 3 new file named “a2.u1.text” 
CORO OK OK ROKK CKO KOK KK ROK KKK 4K XK 3: 

{ i nt roo dou i « ¢t i Oo on + 

OOK OOK OKO OKO OK KKK KKK KOK OK KKK K 3: 


PROCEDURE introductians 
REGIN 


£ 


Wipes t clear screen 


wreitelnmy 

wreiteln ("Welcome to miniadventure!")3; 

wreiteln ("Your goal will be to find a treasure’); 
writeln (Cand bring it back ta your starting"); 
writeln (*point. You will also get points’); 
writeln ("for finding each lacatian in the*); 
writeln (adventure. Foints will be deducted’); 
wreiteln (far various undesirable happenings: *); 
writeln (waking the agre, getting eaten,*); 
writeln (“getting toasted, etc.*); 

wreiteln 
writeln (*I will guide you and be your eyes")+s 
writlen (and ears. Command me with one oar’); 
writeln ("two word phrases, such as"); 

writeln (" "CARRY KEY" or "“NORTH". 7) 3 

writeln ("I anly look at the first letter’); 
writeln (’of commands that tell me which"); 
wreiteln (direction to take. Thus, i take’): 
writeiln (? "NORTH" and "N" toa be the same.*) 3 
writelns 

writeln (* When you are ready ta begin your’); 
weiteln (adventure, just press RETURN"); 


” 


readin (command) 


wipe; 


rd 
=2 


END ¢€ PROCEDURE introduction 


81 


JOO OKOROKROK ROKK KKK OK KOK KOK KOK KK KKK KF 
¢ i rn i t i a 1 i z =] 3 
OOO OOOO ORK GOK KOK KKK KOK KKK} 


PROCEDURE initialize; 


VAI 
locs raoams; 

BEGIN 
location := start; 
dchars Pe is es a — er he ro 
dane := false; 
quit := false; 
cooked r= false; 
eaten r= false; 
lit := false; 
awake := falses 
readmsq = false; 
carrying := false; 
trapped := false; 
dropped := false; 
candig := false; 
chgloc r= trues 
turns = OF 
indark f= OF 
hasduq r= O% 


FOR loc := start TO nowhere DO 
RBREGIN 


visitedClac] := fa 
whatsherelloc] := 


stash s= CI; 


Csandwichdis; 
Chottleds 


whatsherelCdeadend] 
whatsherelmudroaamd 


whatsherelstart] = Clampd; 
whatsherelCcoldraom] = Eshovel 1; 
whatsherelCnarraw4] = Chey; 


cmdnamesCcarry] : 
cemdnamesCdropd r= "drop? 
cmdnameslhelp 4 H 
cmdnameslClight] g 


82 


cmdnameslinvent] = “inventory’s; 


cmdnamesCtaked *take"s 
cmdnames tally = "scare’ 3 
cmdnamestCpush J = "push? 4 
cmdnamesCdiqi = "dig"; 
codnamesllaak J = *jJoaak’* 


a 
= "apen” sy 
= "unlock 3 
= “@at" y 
= "sentinel"; 


cmdnamesCopend 
cmdnameslCunlockd 
cmdnamesCeat J 
cmdnamesCnacmdd 


aeons ms oes ts 23 8s me ose BS 


ob jnamesllampd = “lamp* s 

eb jnameslCtreasurel:= “treasure’;y 
objnamest€ shovel] := *shovel*; 
ob jnameslkey r= “khey"s 

ob jnamest(sandwichi:= “sandwich?’ 5 
abjnamesCbottle] := “hoattle’; 
ob jnameslnoobj)] := “sentinel’ ; 


reset (xfile, *“a2.db80.x*) 


“a2.db80.x” and “a2.db80” are the database files used 
reset (narrate, *a2,db8o*) 


by Adventure 2 for descriptions. 


loc := start; Their location on disk is hard-coded into the game, and 

seek (xfile, 31); Pascal will only look there. As written, Pascal expects 

get (xfile); these files to be on the disk in Drive 1. See “A note 

placesfClocd] := xfile ; about disk Drive numbers in file names and commands” 
on page 3 of this PDF for more information. 

REPEAT 


loc := suce (lac); 
get (xfiled; 
placeslCloc] := xfile™; 


UNTIL, loc = flames; 
close (xfile); 
END ¢ PROCEDURE initialize 3}; 


(OOO OOO OIOOIICIOIO CIO GOK KOK 3 


{ 5s h oF w Oo 6b ji ec t $8 3 
COORG OK K KKK 3 


PROCEDURE showobjectss; 


VAR 
lobj: objectss 


83 


BEGIN 


FOR labj := lamp TO noabj ba 
IF lobj IN whatsherellocationd 
THEN 
REGIN 


write ("There is a *)3 
write (objnamesClabjd); 


writeln (* here." )5 


END ¢{¢ IF lobj IN .. 3 
{ aenddo 3; 


END ¢ FROCEDURE showoabjects 233 


COOK OOOO KRACK KOK KK KKK 5 
£ c=-9 h O w } 


CeCe eeeeese se cesses sce Ses seeles 222255 2 2 2 3: 


FROCEDURE show(where: rooms) & 
VAR 
i: INTEGER: 
BEGIN 
IF (chgloc AND lit) 
OR 
(location = start) 
THEN 
BEGIN 


WITH places{where] DO 
REGIN 


FOR i s= dbegin TO dend bO 
BEGIN 


seek (narrate,1); 
get (narrate) ; 
write (narrate™) | 
END ¢ DO 353 
END <¢ WITH places 3; 
END ¢ IF chglac +f; 


84 


showob jects; 
END ¢€ PROCEDURE show 3 


CKKK KKK KKK KKK KKK KKK KKK KKK KKK KKK KKK KKK 
{ S Cc Q r e } 
CK RK KKK KKK KKK KKK KKK KKK KKK KKK KKK KKK KK KG 


FUNCTION score: INTEGER; 
VAR 

locs roams; 

SCs INTEGER s 
BEGIN 


Sa s= OF 


FOR loc :* start TO flames bdQO 
IF visitedClaci] 
THEN 

Sc. 8 sa + & 

{ endif +} 

{ andda 4 

TF NOT quit 

THEN 
sc = sc + 1405 


IF caoked 


THEN 

SC #= sc = nil) § 
IF eaten 
THEN 

Sc 8= sc - nO g 


IF awake 
THEN 
ae 


cor= sc ~ Bis 
SCOre r= scx 
END {¢ FUNCTION score ?}3 


COCO OOOO OOK OK OKI OK OK 3 


{ congratudlatians 3 
OOOO OOO OOK HOOK OK IOKOK OK 7}: 


85 


FROCEDURE cangratulatians; 
BEGIN 


IF NOT cooked 
THEN 
BEGIN 


IF NOT quit 
THEN 
BEGIN 


writeln (° KKKKK Congratulations *xXKKK"); 
writelns 

write (*You got the treasure aut in anly *)3 
writeln (turns:4,* turns." )¢§ 


END; 
writeln (* You scored *, score:4)3; 


writeln (° points out of a maximum of FOO pts.*)3 
writeln ¢(*So0 long for now, came again saon!*)¢; 


END 
ELSE 
writeln (Sorry about that ~ try again soan!*) 


{ endif 33 


readin (cammand) ¢ 
Wipes 


END ¢{ FROCEDURE cangratulatioans 33 


COOK OKOOKRKOKKOK OK KOKOK KOK KKK} 
{ ao b ji dl o oo k up 3 
CROOK GOO COKOK KK XK KKK} 


FUNCTION oabjlookup: objects 
VAF 

lobj: objects: 
REGIN 


abjnameslnoeobj] := tail; 
labj := lamp; 


86 


WHILE tail <2 abjnamesllobj] pa 
REGIN 


{ Old debug statements ~ leave in 
for historical edification. 

write (tail); 

write (oe): 

writeln (objnamesllobj]); 

+ 


lobj := succ(lobj)s; 


ab jloakup := lobjs 
END; 


COCO OOOO OK OOK OK OK 2: 
5 


t c k oO Fb j @ c¢ t 3 
OC OOKIORO OOOO OK Kok? 


FUNCTION ckobject (it:objects): BOOLEAN; 
BEGIN 


ckobject := false; 


IF it IN whatsherellocation]) 
THEN 


ckobject := true 
tC endif 34 


END ¢ FUNCTION ckhobject 33 

{ source FILE: a@w.us.text 3 neat Gleam So tent 
COO OKO ORK KOCK KOK OK KOK KK 3 
{ p C a r er y } 
COCO OK OK KOK ROOK ROK OK CK KOK KOK >: 


FROCEDURE pearry; 
VAR 
its ab jectss 
BEGIN 
it s= abjlockups; 
IF NOT ckobject (it) 


87 


THEN 
BEGIN 


write (*'I don*’*t see any 
write (tail); 
writeln (¢(* here.’ )¢s 


END 
ELSE 
BEGIN 


writeln (7 Qk"*)s; 

stash := stash + Cit; 

whatsherelClocation] := 
whatshereClocationgy 


END ¢€ IF NOT it IN «wan 35 


END ¢{ PROCEDURE pearry 33 


")s 


Citd 


COO OO OK OKKK KK OK KKK XK} 


{ 


d r Q 


FROCEDURE pdrop; 
VAR 


its ob jects; 


BEGIN 


88 


it = objlookups; 


IF NOT (it IN stash) 
THEN 
BEGIN 


write (*You are not carrying any 


wreitelm (tail)ds 


END 
ELSE 
BEGIN 


writeln ("Ok") 3s 

stash := stash ~ Citds 

whatsherelClacatian] := 
whatsherelLlocations 


So 


Pp ) 
OOOO OOOO OOOO OOIOICIOIGIGOO GIGI? 


citds 


as 
2 


*) 


END {¢ IF NOT it IN stash 3; 
END ¢ PROCEDURE drop 33 


COCO OOO GOK OKO OK KOK KK D 
{ p h e 1 p } 
StSCCCCCeCesess Se Ce ses Less ss FF 2 2 5355 8; 


FROCEDURE phelp; 
BEGIN 


IF readmsg 
THEN 
BEGIN 


writeln (*The treasure is deep in the maze’); 


END 
ELSE 
BEGIN 


writeln (* There are hints to be found’); 


writeln (*near the lake and in the alcove.’); 


END { IF readmsg 33 
END ¢ FROCEDURE phelp ?}3; 


COO OOOO OOOO GOK TOK OK KKK > 
{ p 1 i g h t 3 
OOOO IIOIOR OIG OOO OK KOK AOK > 


PROCEDURE plight; 
VAR 

cmd: STRING; 
BEGIN 


IF NOT (lamp IN stash) 
THEN 
writeln (You are not carrying the lamp.*) 
ELSE 
BEGIN 


IF (tail *on*) 
OR 
(tail = “lamp’*) 


89 


THEN 
lit s= true 
ELSE 
lit := false 
{ endif ts 
END ¢ IF NOT lamp IN stash 33 
IF lit 
THEN 
writeln ("Your lamp is now on.”*) 
ELSE 


writeln (* Your lamp is of f.7)¢s 


$ 
Ps 


{ endif 33 


END { FROCEDURE plight 33 


colle 


CK RK KKK KK KKK KKK KKK KKK KKK KK KKK KKK KKK KS 
¢ Poionv @ nm ta riey + 


ORR RR KKK ORK KKK KKK KK KKK KKK KKK KK KG 


FROCEDURE pinventorys 
VAR 

lobj: objects; 
BEGIN 


IF stash <2 C7 
THEN 
BEGIN 


writeln (* You are currently holdings 


FOR lobj := lamp TO noobj DO 
BEGIN 


IF lobj IN stash 
THEN 

writeln (abjnamesllobj]) 
{ endif } 


END ¢ FOR lobj := 
END € IF stash ¢3 CJ 


END € FROCEDURE pinventory 33 


{ 


CK KKK KKK KKK KK KKK KKK KKK KKK KKK KK KKK KK KD 


90 


COO OOKKAOK KK KKK KK KK KKK KKK KS 


ut $ h 3 


- 


PROCEDURE ppushs 
VAR 


newtails STRING; 
ps INTEGERS: 
REGIN 


p := pos (* *, taild; 
IF p = © 
THEN 

newtail := 
ELSE 
BEGIN 


newtail := copy(tail,ptl,length(tail)d-p)j 
tail := copy(tail,1,p~1); 


END ¢ IF pO 35 


IF (tail = “treasure’*) 
THEN 
BEGIN 
IF (treasure IN whatsherelCnarrowl)) 
AND 
(location = narroawl) 
THEN 
BEGIN 


writeln (7 Ok"); 

whatsherelCvestibule] := 
whatsherel[vestibule] + Ctreasurel; 

whatsherelCnarrow1 ] := 
whatshereCnarrowl] ~ Ctreasureds 


END ¢ IF treasure ws. 35 
END 
ELSE 
BEGIN 


writeln (*Sorry, but I don**t think’); 
writeln (*?I can do that.")3; 


END { IF tail = *treasure® 33 


END ¢ PROCEDURE ppush 335 


91 


OOO KOK OK RR KOK KKK KKK KK KK KKK KKK KKK KG 
t p d i g 3 
CORO ROKK KKK KK KKK KKK KKK KKK KKK} 


PROCEDURE pdig; 
BEGIN 


candiq := (shovel IN stash) 


AND 
Creadmsaq) s 


IF (lacatian = mi) 
AND 
candig 

THEN 

hasdug := hasdug + i 

{ endif 33 

IF (leacation <3 m19) 

THEN 

writeln ("You can**t dig here at all.’) 

ELSE 

IF hasdug = 0 
THEN 
BEGIN 


writeln ("You can’*t dig here yet.")s; 


END 

ELSE 
IF hasdug = 1 
THEN 
BEGIN 


writeln ("*That**’s a nice pile af dirt you"); 
writeln (*have shovelled. Re careful"); 
writeln ("not te block your way out! *")3 


END 
ELSE 
IF hasdug = 2 
THEN 
REGIN 


92 


writeln (7?I see the top of a weather--*); 
writeln ("beaten chest.” )s 


END 

ELSE 
IF hasdug = 3 
THEN 
BEGIN 


writeln (* You have unearthed an old"); 
writeln ("treasure chest. Tt is’); 
writeln (*secured with a massive"); 
writeln ("brass lock.")s 


END 

ELSE 
IF hasdug = 4 
THEN 
REGIN 


writeln (* There’ *s nothing else here.’ 


writeln (but if you try hard, you"); 
writeln ("might diq dawn ta the*); 
wreiteln Clava pits!")43 


END 
ELSE 


IF hasdug = 4 
THEN 
BEGIN 


cooked s= trues 
dane f= trues 
show (flames) ; 
lacation s= flames; 
chgloc f= trues 


END ¢ IF hasdug = 4 3 
{ END IF hasdug = 3 3 
{ END IF hasdug = 3 3} 
{ END IF hasdug = 
{ END IF hasdug = 1 


> 2 
ar) 
4, 
2 


) 


93 


{ END IF hasdug = O 3 
{ END IF (location #2 m1i9 3; 


ny 


END {< FROCEDURE pdig 3}; 
COOK IOK GOK KK KKK ROKK KK KKK AK KKK KD 


{ Pp oO p ein 3 
COROROK KOK K KKK KKK AK KKK KKK KKK KR KKK KKK KD 


PROCEDURE popens 


BEGIN 

IF (location = m19) 
AND 
(hasdug += 3) 
AND 
(key IN stash) 

THEN 

BEGIN 


writeln (*Ok*)s 
whatshere(€m19] := whatshere(€m19] + Ctreasured; 
writeln (*The chest is full of treasure’); 


END ¢{ IF 3; 


IF (lacatian = mi?) 
AND 
(hasduqg == 3) 
AND 
( NOT (key IN stash)) 
THEN 
BEGIN 
Cl*'m afraid you’"11 need a key*)s 


writeln 
(*ta open that brass lack!")43 


wreiteln 
END {€ IF 33 
IF location «<2: mil? 


THEN 


wreitelnm ( There’ *s nothing here toa apen!*) 


{ endif 3; 
END { FROCEDURE popen 34 


94 


COICO KOK KK RK OK OK KOK KKK} 
¢ Pp tla ai ik } 
CORR ROK ROKR KKK KOK KK XOKK KKK KK KKK KKK} 


FROCEDURE plaoks 
VAR 

savcehaqs BOOLEAN; 
BEGIN 


gsavchg : chqgloacs 
chqaloc := trues; 


IF location «<= flames 
THEN 

show (locatian) 
ELSE 


END ¢ FROCEDURE plook 3}; 


CORO KKK RK KKK KR KKK KK KKK KKK KD 


{ p e a t 3 
CROOK KK KK KKK KK KKK KKK KK KKK KK KD 


FROCEDURE peat: 
BEGIN 


IF sandwich IN stash 


THEN 
BEGIN 


writelnm (Oh, yummy! !*") 3 
Stash := stash ~ ECsandwichds 


END 

ELSE 
IF tail = “sandwich’ 
THEN 


writeln (* Yau don*"t have a sandwich’) 


ELSE 


writeln (* Don’ *t be ridiculous! !*) 


+ 


{ endif 3 


95 


{ endif 3 
END ¢ PROCEDURE peat 33 


CORO OKO KK HO KKK OK 3: 


¢ c m gd lof ag k ww p 3 
COCO ROOK KK KOK KOKORO KKK KKK KK KG 


FUNCTION cmdloakup: cmds; 
VAR 

p: INTEGER; 

lemd : cmdss 
BEGIN 


wreitelms 

write (7 === 2"); 

readin (command) ; 

p := pos (* *, command); 


IF p=0 
THEN 
BEGIN 


= copy (command, 1, pl); 
tail := 


copy (command, pti, length (caommand)~p)s 


END ¢ IF p=0 3}; 


cmdnamesCnocmd] := head; 
lemd := carry: 


WHILE head <> cmdnameslClemd] DO 
lemd s= suce (Clemd) 
{ END DO }3 


cmdlookup := lems; 
END ¢€¢ FUNCTION cmdlookup 33 
COOK COIR CCK ROKOKOK OK KKK KK KD 
{ 1 i $s t ein + 


COO O OOOO OK OK OK ROK KKK OK OK KK KD: 


96 


PROCEDURE listen; 
VAR 

lemd: cmdsy; 
BEGIN 

REFEAT 


lemd := cmdlookups 
CASE lemd OF 


carrey: pearrys 

drap:s pPdraps 

helps phelps; 

light: plight; add a space between 
x ares pinventorys comma and apostrophe 
takes pearrys 

tally: BEGIN J 


write ("*Ghould you quit mow," )3 
writeln ("your score would be *)5 
wreiteln ((score 140) :3)3 

writeln (* points of a possible 300,’ 


END; 
pushes ppushs; 
dig: pdiqs; 
loaks ploaks 
opens popens; 
unlocks popen; 
eats peat; 
nocmds $ 


END: 
UNTIL lemd = nocmd; 
END ¢ PROCEDURE listen 33 
COOK OOOO OO OOK OOK OK OK x 3 


{ do «c¢ o m m aon ad } 
COO ROOOK OKRA KK KKK K KKK KKK KKK KKK KK KD 


FUNCTION docommand: CHAR; 
BEGIN 


head s= "75 
tail r= 7" 5 
REFEAT 
listen 
UNTIL length (head) = Of; 


docommand := headCilds 
END ¢ FROCEDURE docommand 133; 


COOK OK KKK OK OK KOK KK} 


, 


{ w Hh i c¢ Hh w aiey 3 
COR ORK KOK KK KKK KKK KK KKK KKK} 


FUNCTION whichway:directions; 
BEGIN 


turns = turns + 1; 
REFEAT 


ch := docommand; 


CASE ch OF 
*n's whichway := m3 
*@?s whichway := s3 
"e's whichway := eF 
7wis whichway : = wy 
7? is whichway s= us 
"d’"s whichway s= ds 
?q's quit s= trues 

END; 


UNTIL ch IN dchars; 
writelns; 
end ¢ function whichway 3; 
CK KKK KK KKK KK RK KKK KKK KK KKK KKK KKK 


{ n Oo w a y } 
CORR KKK KKK RK KKK KKK KKK KKK KKK KKK KK) 


98 


add space betwen 
procedure noways go and apostrophe 


begin | 
writelns; 


write (*There is no way to go"); 
writeln (’in that direction.’)s 


chglac := false; 
END; 


COO OO OK OK KKK OK OK K 


t Cc kk 1 a m Pp } 
COO OOOO OK OK KOK OK ORCK OK KOK 3 


PROCEDURE cklamps 
BEGIN 


IF (location «<> start) 
AND 
NOT lit 
THEN 
indark 3: 
{ endif 35 


IF indark = 4 


THEN 

RBREGIN 
quit s= trues 
cooked = trues 
dane r= trues 


writeln ¢(* You fell into a pit and were’); 
writeln ("*killed. Too bad!! Maybe next’): 


writeln (time you"’11 listen to me*); 
writeln (‘and keep your lamp on!"); 


END 
ELSE 


IF (indark 3} ©) 
AND 
(NOT lit) 
AND 
(lacation “+ start) 


99 


THEN 
REGIN 


writeln ("It is now pitch dark.") 3 
writeln ¢(* I wouldn’**t go too far.*); 
writeln (* You might fall into a pit AND 
writeln ("killed.")s; 


END ¢ IF indark + © 33 
{ END IF indark + 4 3; 


IF (turns += 75) 
AND 
(turns < 80) 
THEN 
BEGIN 


be" )¢ 


writeln (7? I would think about wrapping this"); 


writeln ("up, since your lamp is getting dim. 


END 

ELSE 
IF turns = 150 
THEN 
REGIN 


wreiteln (You’?re in trauble mow! *)5 
weiteln (Your lamp just went aut! *)¢3 
lit = false; 


END ¢{ IF turns = 150 3 
{ END IF turns = 7S wae FY 


END { FROCEDURE cklamp 3; 


OOOO KKK OK OK ROOK KKK KK > 


£ t r a Vv e il 3 
COOOOROROKKOOK KK KOK K KK KK KKK KKK KK KK KD 


PROCEDURE travel ¢ 
nloc, 
sloc, 


eloc, 
wloc, 


100 


2 


7) 


uloc, 
dloc: rooms) ; 


PROCEDURE newloc(loc:roams) 
REGIN 


IF loc=nowhere 
THEN 


chgloc 
END ¢ IF 33 


END { FROCEDURE newloc }3 
BEGIN {kx travel xx} 


CASE whichway OF 
ms newloc (nlac); 
gs: newloc (sloac)s 
@: newloc (eloc); 
we newloc (wloac); 
us newloc (ulac)s 
ds: newloc (dloc); 


END ¢ CASE whichway 33 


END (¢ FROCEDURE travel 33 

{ source FILE: a@.m2.text 3 
COOK OKOK KKK KKK KK KOK KK KG 
if oO gr @ a c t i oO nn 3 
COKOOKOKKROK OK KKK KOK KK KK KKK KKK KKK KKK KG 


new file named “a2.m2.text” 


PROCEDURE ogreaction; 
REGIN 


IF NOT awake 

THEN 

BEGIN 
writeln ("This is the ogre’*s lair!"); 
writeln (If you are not careful, you’? 11"); 
writeln (*wake him.*)3; 


IF (turns MOD 3)=0 


101 


THEN 
BEGIN 


awake := trues 

writeln ("Now you" "ve dane it!")s5 

writeln (*You woke the ogre - better’)s; 
weiteln ("get out of here while you can’); 


writeln (* You wouldn’*t listen ta me would’); 
writeln ("you? You really better get out’); 
writeln (of here before you get eaten! *), 


IF treasure IN stash 
THEN 
IF (turns MOD 2)=0 
THEN 
BEGIN 


writeln (*Too bad!! The ogre caught you"); 
writeln ("and roasted you for dinner.’): 
wreiteln ("Better luck next time!!")s3 


eaten = trues 
quit  o:= trues 
END 
ELSE 
BEGIN 


writeln ("Get out fast if you don*."t want’); 
writeln (to be a big-mac far the ogre! !")45 


‘Big Mac’ if we’re getting technical... 


END 
ELSE 
IF (turns MOD 2)=0 
THEN 
REGIN 
writeln ("Too bad ~- you" "ve been eaten! "); 


102 


eaten 2 trues 
quit f= trues 
END 


Q %. 


{ endif 3} 
END ¢ IF NOT awake 33 

END ¢ FROCEDURE ogreaction 33 

COCO OOOO KIO KOK KOK KOK K 7: 

t p S t. a r t > 


OOOO OKO KKK KOK OK 3: 


FROCEDURE pstart; 


REGIN 
IF treasure IN stash 
THEN 
done : = true 
ELSE 
REGIN 


CASE whichway OF 


Ms Bp @aWe noways 


us writeln ("You can**t jump to the clouds!*); 


d: location := vestibule; 


END {€ IF carrying f3 

chgloc := (location «<> start); 
END ¢ PROCEDURE pstart 343 
COOK GOK OK OK KOK 3 
if p v @ Ss t i bh u | @ » 


ORO ROKK OK OK OKKOKOK ROK KOK KOK KK KK KD 


PROCEDURE pvestibule;s 
BEGIN 


103 


IF treasure IN whatsherel€narrowl] — addspace between 
THEN narrow and apostrophe 
BEGIN 


write (’To the north, through a narrow’); 
writeln ("crack,*)3 
writeln ("you can see the treasure. ")3 


END { IF treasure ... 33 


travel (narrowl, grandroom,iceroom, 
nowhere, start,nowhere) ; 


END {¢€ FROCEDURE pvestibule 3; 


COOK KOK ROKK KK KOK KK KK KKK 
t Pp na rr aiew i il 3? 


COCO OKO KOKORO OK KOK K 3} 


PROCEDURE pnarrowl: 
BEGIN 


CASE whichway OF 
= lakeshore; 

ogreroom;: 

*?s too narrow to get through! ")s; 


ne location : 
es location =: 
Ss writeln (?* 


It 
wWyltads noways 

END ¢ CASE whichway 73; 

chgloc := (location “<> narrowl)s 


END { PROCEDURE pnarrowl 33 


COO OOK OOOO KOK KK} 
{ p i 5s lt aooni qd 3 
OOOO OOK OKO KK KK KK KKK 


PROCEDURE pislands 
REGIN 


travel (nowhere, lakeshore, nowhere, 
nowhere, nowhere, nowhere) 4 


readmsqg := trues 


104 


END { FROCEDURE pisland ty3 


OCR KOO ROOK OKO HOOK KOK 7 
t Q g r e r Q Q m } 
COOK KOO OK GK KKK OK KKK 3: 


PROCEDURE pogreroaaoms 
VAR 

re INTEGER: 
REG IN 


agreactions 
IF NOT eaten 
THEN 

BEGIN 


wreiteln ("There are exits to the east,"); 
writeln ("*north, and west"); 


CASE whichway OF 


Ws location = narrowl: 
e: location := batscaves 
ns location :* narrow2; 
ds: 
BEGIN 
quit 2 trues 
eaten 8 = trues 
writeln (?Oh no!!! You dummy!!!" )s 


writeln ("You just fell into the firepit’): 
writeln (and made such a ruckus that"); 
writeln ("you woke the ogre. I hate to’); 
writeln (’tell you this, but you are’); 
writeln ("also trapped!’); 


BEGIN 
FOR j s= 1 TO 1000 DO; 
write ("2.7") 3 

END { DO 33 

writelngs 


writeln (*You"’* ve just been added to the’); 


105 


Cogre’’*’s gourmet recipe library!*)3 


writeln 
(Better luck next time.*)s 


writeln 
ENDs 


Salk NOWwAYs 


woe 
v8 


END {£ CASE whichway 


END ¢ IF NOT eaten 33 


chgqloc := (location “> agrerooam) ; 


END { FROCEDURE pogreroaam 33 
new file named “a2.maze.text” 


% 


{ source FILE: aZ.maze.text 3} 
COKK OKRA KKK KKK KKK KKK K KKK KKK KKK KKK KK KG 
{ p m a z e } 


COOK OKK KK AK KKK KKK KK KK KKK KKK KKK KK KK KD 
PROCEDURE pmazes 

TYFE 

Mazerooms = mO,.same; 


VAR 


mazelac: MAZeroamMss; 


FROCEDURE describe; 


BEGIN 
writeln ("You are in a maze of *)s 
writeln (’featureless passages." ); 
writelns; 


end { procedure describe 3; 
procedure sameplace; 

begin 

(7? You have crawled around’); 


{*some twisted tunnels and*); 
(*7wound up where you began.”*)3; 


writeln 
writeln 
writeln 


% 


END { PROCEDURE sameplace 33; 


FROCEDURE travel (¢ 


nloc, 
sloc, 


106 


eloc, 
wloc, 
uloc, 
dlocs rooms); 


FROCEDURE newloc (loacrrooms) ; 
BEGIN 


IF loc=moO 
THEN 
noway 
ELSE 
IF loc=same 
THEN 
sameplace 
ELSE 
location := loc 
{ endif 3 
{ endif 33 


END ¢ FROCEDURE newloc +3 
BEGIN (xk travel XxX? 
CASE whichway OF 
ns newloc (nloc)s 
Ss newloc (sloc)s: 
e: newloc (eloc)s 
we newloc (wlac)s 
us newloc (uloc); 


ds: newloc (dloc); 
END { CASE whichway 33 


END ¢ PROCEDURE travel 33; 


PROCEDURE pmls 
BEGIN 


writeln ("From here you can go south, 
writeln (west, or up.") 3 


CASE whichway OF 


Ss location := ladders; 
e: location s= m2; 


east.” )3 


107 


us location 
nads naways 
END; 
END { pmi 33 


ws lacation 


BEGIN <¢ FROCEDURE pmaze 34 


REFEAT 
IF (location “> mi) 
AND 
(location <> maze) 
THEN 


108 


describe 
{ endif 3¢ 
showoh jects; 
ckhlampy 


CASE locatian OF 


MAZE, 
mis pml; 
ms travel 
mos travel 
m4s travel 
ms travel 
més travel 
m7: travel 
m8: travel 
mo s travel 
mids travel 
mids travel 
mi2s travel 
miss travel 
mi4: travel 
mics: 

BEGIN 


(m1, Same, mM, mO,MO,MO) 3 
(m1, m0, same,mO,mO,mO) 5 
(mO.M7 4 M34 MF, MO, MO) § 

(m1,mO,MO,MO,MO,mMO) 3 

(m4,m0, same,~mO,mO,mO) 3 
(mS,m?,mo,mB,~mO,mO) 5 

(m,MO,MO, SAMS,MO,MO) § 
(mO,M11,MmO,M1O9O,MO,MO) § 
(mB, Same,MmO,~mMO,mMO, MO) 5 
(m?,MO,MG,M1O,mi,~mi2);: 
(m12,mO,MmO,~MmO,MO,m14G) § 
(m14,mO,mMO,mMO,MO,m17) | 
(m1S,mO,mo,mO,mO, M18) 


writeln (° I seem to remember buried’); 


writeln ("treasure near this location.”*) 


travel (mO,mO,mO,mO,m1,~mM1?) 


END ; 


midé: travel (m17,same,mO,mO,mO,mO) ; 
mi7: travel (m18,.m16,mO,MmO,MO,mMO) 5 
mis 


BEGIN 


writeln (’I seem to remember a treasure’); 
writeln (*near here.*)3 
travel (m19,m17,mO,mO,MO,mMO) 


END; 
mivs travel (mO,m18,mO,mOo,m1S,mO) : 


END {¢ CASE location 3, 


UNTIL (location «< maze) OR (location = flames): 
chqloc := trues 


END ¢ FROCEDURE pmaze 33 
{ source FILE: a@.main.text } new file named “a2.main.text” 


OOOO OOOO OO OOOO II IIE: 
£ p p i tt } 
OOOO OO OOOO OGIO IO OOK IK 3 


FROCEDURE ppits 
BEGIN 


CASE whichway OF 


d: location := ladders; multiple statements 


us x writeln (*Try ta climb that’); in a case selector 
writeln (Cand you’ "ll kill yourself!*)s; must be bookended 
END; by BEGIN and END; 
Ty Sa @qgws naways 
The BEGIN statement must be added 
END; between u: and writeln as 
chgloc := (location <> pit); indicated by the asterisk. 
Type “u: BEGIN” and press return. 
END ¢€ FROCEDURE ppit 35, Tab over under BEGIN and continue 


typing the writeln instructions, 
CROOK OK CK KOK KKK KKK KKK followed by “END;” as shown. 
{ Pp tladqddeor } 
COC OOOO OOK ORK KKK KK} 


FROCEDURE pladder; 
BEGIN 


109 


CASE whichway OF 


ms location t= maze; 
d: location := flames; 
us 


IF treasure IN stash 
THEN 
BEGIN 


writeln (You can’’*t carry the treasure"); 


writelnm ("up the ladder -")3 
writeln ("it*"*s much toa heavy! "); 


END 
ELSE 

location := vestibule 
{ endif 33 


@,.S_Ws NOwWAYy s 


END; 


chgloc := (lacation «<> ladder) 3: 


+ 


END ¢ PROCEDURE pladder 33 
CREST EESRE EDEL EET CEES EL LESS EERE SESE RS 


{ p ag 1 a m e 3 


STUTTTETTRTTTTUTTTTTETTTT TET TOTTORI TS 


FROCEDURE pflames; 
BEGIN 


cooked := trues 
done f= trues 


END {€ FROCEDURE pflames 3}; 
COOK OOK OK KOK OK KKK OK OK} 


BEGIN { =ee==> adventure {ss=== } 
CKKKKKKKKKRK KKK KKK KKK KKKK KG 


introduction; 
initialize; 


110 


REPEAT 


visitedClocatiand := true; 
show(locatioan) 3 


cklamp; 


CASE location OF 


start: 
grandroams 


vestibule: 
narrowls: 
lakeshore: 


islands 
brink: 


iceraoms 


ogreraams 
narroaw2s 


pit: 
crystals: 


batscave: 


steams: 


deadend: 


ladder: 
Mazes 
stairs: 


pstarty; 

travel (nowhere, 
nowhere, 
nowhere, 


pvestibules 
pnarrawl; 
travel (island, 


narrow? , 
nowhere. 
pislands 
travel (nowhere, 
nowhere, 
nowhere, 


travel (ogreroom, 


crystal, 

nowhere . 
pogreraams 
travel (nowhere, 


nowhere, 
brink, 
stairs): 


narrowl, 


nowhere, 
nowhere) 5 


nowhere, 
ogreroom, 
pit); 
nowhere. 
vestibule, 
nowhere) 


ogreraom, 


steam, lakeshore, 


NOW Er & . 
ppits 


travel (ogreraom, 


nawhere) 3 


nowhere. 


Maze, agreraam, 


nowhere, 


nowhere) 3 


travel (steam, moawhere, 


nowhere, 
nowhere, 

travel (deadend, 
nowhere, 
nowhere, 

travel (nowhere, 
nowhere. 
nowhere, 

pladder; 

PMaAZes 

travel (nowhere, 
nowhere. 


ogreroom., 
nowhere) 
batscave,. 
narrows. 
MAZE) § 
steam. 
nowhere, 
nowhere) § 


nowher &, 
nowhere. 


111 


END ¢ 


112 


echoes: 


inclines 


warmrooms 


roundrooms: 


honeycomb: 


mudroams 


deeppool: 


caldroom: 


narrows: 


narrow4s: 


rivers 


rockyrooms: 


siltrooms 


alcoves 


flames: 


CASE 


grandraom, 


travel (warmroom, incline, 
nowhere, nowhere, 
stairs, deeppool); 
travel (nowhere, nawhere, 
roundroam, honeycomb, 
echoes, nawhere) 3 
travel (nowhere, echoes, 
nowhere, nawhere, 
nowhere, flames); 
travel (honeycomb, nowhere, 
nowhere, incline, 
nowhere, mudroam) § 
travel (nowhere, narrows, 
incline, nowhere, 
nowhere, nowhere) s 
travel (nowhere, nowhere, 
nowhere, nowhere, 
roundroam, siltroom)s: 
travel (nowhere, nowhere, 
nowhere, nowhere, 
mudroom, coldroaam) ; 
travel (nowhere, nowhere, 
nowhere, nowhere, 
deeppool, nowhere): 
travel (honeycomb, narrow4, 
nowhere, nowhere, 
nowhere, nowhere) ; 
travel (narrows, river, 
nowhere, nowhere, 
nowhere, maze) s 
travel (narroaw4, rockyroom, 
nowhere, nowhere, 
nowhere, nowhere); 
travel (river, siltroam, 
alcave, nowhere, 
nawhere, nowhere) 5 
travel ¢rockyream, nowhere, 
nowhere, alcove, 
muidraam, nowhere) ; 
travel (nowhere, nowhere, 
siltroom, nowhere, 
nowhere, nowhere) 3 
pflames; 


+ 


location 33 


echoes) § 


UNTIL quit OR done; 
cangratulationss 


END. 


113 


Command Processing in Adventure 2 


This chapter is one of the most important of the 
book. It deals with topics that, taken one at a time, 
are pretty simple, but when put all together form a 
major part of all our subsequent adventure game 
implementations. You will probably want to read it 
more than once. It is also a good idea to refer to the 
actual Pascal code frequently while reading. 

The first example of an adventure game would 
be considered a mere “toy” by devotees. Why? 
Mainly because it did not allow the player to use 
traditional adventure game commands. It limited 
the player to travel commands or directions. The 
consequence of this was the fact that “solving” all 
the problems was totally unchallenging. It was sim- 
ply a matter of answering yes or no questions (with 
most of the successful answers obviously being 
yes). Of course, you want your own adventure 
games to allow “real” command processing. 

Commands are important because they add a 
sense of control to the game. The player is par- 
ticipating. Not everything is yes or no. Even the 
commands that the guide will recognize and carry 
out must be guessed by the player. Beyond that, 


commands provide the vehicle by which players 
solve the problems posed by the adventure game. 
This is the essence of true adventure, and without it 
adventures are not really worth playing. 

In Adventure 2 I have included commands that 
take the form of one or two word sentences. These 
are simple verbs or verbs with a single noun as 
object. Such commands provide adequate complex- 
ity. More advanced adventure games allow a vari- 
ety of multiple word commands. Such games often 
claim that the player can use English commands. 
This is an overstatement. Most English sentences 
will completely befuddle such adventure games. In 
any case, implementing a parser for such commands 
is a much greater challenge than I am prepared to 
attempt in this book. So I will stick to our one or two 
word commands. Even these will allow you to im- 
plement interesting problems in your adventures. 

The commands that have been added to the 
game are carry, drop, help, light, inventory, take 
(synonym for carry), tally, push, dig, look, open, 
unlock (synonym for open), and eat. 

There is one support procedure for each possi- 


115 


ble command. These procedures are named by pre- 
fixing the letter p to the command name itself. So 
for example, there are the procedures pcarry, 
pdrop, plight, and so on. The command support 
procedures are to commands, what the location 
procedures are to locations. They provide all the 
Pascal code needed to carry out the corresponding 
command. The commands that are synonyms for 
other commands use the same support procedures 
as the commands for which they are synonyms. 


AN OVERVIEW OF THE COMMAND HANDLING CODE 


Figure 13-1 shows the procedures and func- 
tions used by Adventure 2 for command processing. 
The top levels of this diagram duplicate Adventure 
1. What is new are the functions and procedures 
below travel and whichway—docommang, listen, 
and cmdlookup, which are various procedures de- 
signed to do special processing for individual com- 
mands. Much of the code that I describe in this 
chapter can be reused in all of your adventure 
games. 


The travel Procedure 


The travel procedure causes either a new lo- 
cation to be determined or the message “There is 
no way to go in that direction” to be printed. The 
travel procedure invokes the whichway function to 
determine which direction the player desires to 
take. I discuss travel in more detail in Chapter 16. 


The whichway Function 


The whichway function is also similar to the 
one used in Adventure 1. It differs in that it does not 
contain the code for prompting the user. Rather 
it invokes a new function called docommand; 
docommand returns a single character that indi- 
cates the direction the player wants to go. There is 
the possibility that this character does not corre- 
spond to one of the legal travel indicators. In sucha 
case, the command is simply ignored and the player 
is reprompted for a command. 

There are two weaknesses with the command 
processing in Adventure 2 that will be remedied in 
Adventure 3: 


116 


gw Any word beginning with a travel letter (n, s, e, 
w, u, or d) is taken to be equivalent to the travel 
command. Thus, if the player types NEVER- 
MORE, the command processor will assume that 
the command was n, or north. 

w@ If a command that is neither a legitimate com- 
mand nor a direction indicator is typed, the 
player is simply reprompted. There is no indica- 
tion given that the guide failed to understand. 

__ This can be misleading at times—the player may 

‘ assume that the command was understood. For 
example, TURN ON LIGHT will be ignored, and 
the lamp, if not yet lit, will stay unlit. 


The docommand Procedure 


The docommand procedure is really a “front” 
for listen, which does the real work. the docom- 
mand procedure sits in a while loop calling the 
listen procedure. The listen procedure eventually 
returns, having set the variables head and tail. The 
docommand procedure returns the first character 
of the head string. It makes sure this is valid by 
first determining that there is something in the 
string to return. This is accomplished by using the 
intrinisic function length: 


length (head) > 0 


The listen Procedure 


The listen procedure is the command dis- 
patcher for Adventure 2. It supervises the process 
of determining which command the player wants to 
have executed. It invokes the appropriate command 
procedure for each command entered. 


The cmdlookup Function 


The parts of a compiler that read, interpret, 
and verifiy the syntax of a program are called scan- 
ners and parsers. The cmdlookup function provides 
a very rudimentary scanner and parser combination 
for Adventure 2. It prompts for the players com- 
mand and breaks that command into two pieces— 
head and tail. The head string always determines 
which command will be executed. The tail string 
will contain the object of the command verb if there 


arection AY 


docommand 
listen 


cmdlookup 


Ka 


Fig. 13-1. The structure diagram of the command processing in Adventure 2. 


is any. It is always possible for tail to be the empty As I hinted earlier, cmdlookup is not at all 
string: length (tail) = 0. sophisticated. It doesn’t even bother to make sure 


117 


that the character strings in head and tail are sensi- 
ble for command and object names. It will blithely 
allow you to type: 


*&ANO$H@W(2>< —--++== 


as a command. When it fails to match the string 
& %# '(/2>< to the name of any legal command 
or direction, it will reprompt you for another com- 
mand. It ismonumentally stupid! However, it is just 
smart enough to do the required job. 


COMMAND PROCESSING IN DETAIL 


Parts of the command processing code involve 
some rather complex but very useful programming 
techniques. These programming strategies are 
detailed in the following pages. 


docommand: Calling listen in a Loop 


There really isn’t much more to say about 
docommand. It conveniently separates an enclos- 
ing loop around listen. That loop could have been 
incorporated into listen itself. The only difference 
would be a miniscule gain in efficiency. Because 
docommand is involved in direct response to in- 
teractive commands, you probably wouldn’t even 
notice the difference. If you are the curious type, 
making that modification would be a good exercise 
to try. 


listen: Another Case Statement for Control 


The listen procedure uses a local variable 
Icmd of type cmds to control its case statement. 
The cmds type is an enumerated type that names 
all the commands available to the player: 


cmds = (carry, drop, help, light, inventory, 
take, tally, push, dig, look, open, 
unlock, eat, nocmd); 


I shall discuss the use of this type and the setting of 
the variable Icmd in detail below. 

The REPEAT . . . UNTIL loop in listen en- 
sures that the player’s commands are carried out 


118 


until a command that is either a direction or an 
unrecognizable sequence is entered. All of the fun 
takes place inside the cmdlookup function, and I 
devote the remainder of this chapter to its work- 
ings. 


The cmdlookup Function 


The cmdlookup function is not very long—a 
little over half a page of well-spaced Pascal source 
code. Yet it packs a lot of wallop. In the course of 
unraveling its inner mysteries, I shall delve into the 
following subjects: 


@ The input of enumerated types. 

@ Arrays indexed by enumerated types. 

@ The conversion of values—external to internal. 

@ The method of searching an array using a linear 
search. 

@ Arrays of strings and string comparison. 

@ Sentinels in a linear search. 

@ The succ function and its use in searching. 


There is a lot to cover. The only way to reach the 
end is to plunge ahead. So, here we go!! 

The Input of Enumerated Types. I have 
done more than a little to give Pascal’s enumerated 
types “rave reviews.” Now I come to one of the 
annoying weaknesses of enumerated types in many 
Pascal implementations: it is usually impossible to 
READ or WRITE a value of an enumerated type 
directly, that is, using the identifiers contained in 
the declaration of the enumerated type. 

In Adventure 2, this is exemplified by the array 
of strings that I have called cmdnames. cmd- 
names is indexed by the enumerated type cmds: 


cmdnames: ARRAY{[cmds] of STRING; 


In order to really understand what is going on 
here, I need to elaborate on this declaration and its 
implications for the Pascal language. Bear with me 
while I digress from the consideration of the actual 
code of cmdlookup. 

Arrays Indexed by an Enumerated Type. 
I am assuming that you know what an array is. I also 


assume that you have used arrays before in your 
Pascal programs and are familiar with the syntax for 
declaring arrays. However, the idea of using an 
enumerated type as the index for an aray may be 
new to you. 

Ordinarily, the index of an array is simply a 
range of integers 


x: ARRAY[1 . . 100] OF STRING; 


and individual elements of an array are referenced 
using single values from the range as sub- 
scripts—x[1], x[5], x[29], and so on. An array cor- 
responds to a list of items. The index is like the 
number indicating the position of the item in the 
list. Because the identifiers of an enumerated type 
are assigned numeric values by the Pascal com- 
piler, you may think of the enumerated type itself as 
being just like a range of integers. Therefore, using 
an enumerated type as the index of an array in a 
declaration is a normal thing to do. 

When an array is indexed by an enumerated 
type, you can think of the array as a whole as a 
“named list” of items. To illustrate, consider the 
following list of men’s nicknames. The list is enu- 
merated by the names for which nicknames are 
being listed: 


Name Nickname 
Richard Dick 
Thomas Tom 
Lawrence Larry 
William Bill 
John Jack 
Arnold Arnie 
James Jim 


This list may be represented in Pascal as an array of 
strings indexed by the enumerated type: 


name = (Richard, Thomas, Lawrence, Wil- 
liam, John, Arnold, James); 


The array declaration would look like this: 


nicknames: ARRAY[name] OF STRING; 


Then assignment statements such as 


nickname[Richard] := ’Dick’; 
nickname[Lawrence] := ’Larry’; 
nickname[James] := ‘Jim’; 


are perfectly legitimate. Each one constructs one of 
the entries in the original list of nicknames given 
above. 

The Conversion of Values: External to 
Internal. The identifiers in a Pascal enumerated 
type represent external values. The numbers as- 
signed to the identifiers in an enumerated type 
represent internal values. They are the values that 
the Pascal compiler prefers to use when ma- 
nipulating the enumerated type inside the comput- 


er. 
In the best of all possible Pascal implementa- 


tions, you should be able to write the following sort 
of code: 


writeln (“Your wish is my command > ”); 
read (Icmd); 
CASE Icmd OF 


Carry: pcarry; 
drop: pdrop; 
help: phelp; 
light: plight; 
invent: pinventory; 


and so on. That is, given a variable of type cmds, 
such as Icmd, you should be able to read a value of 
Icmd. The user of the program containing the above 
code should be able to type one of the identifiers in 
the enumerated type in response to the prompt, for 
instance 


Your wish is my command > carry keys 


This should have the effect of the identifier carry 
being read and the corresponding internal value of 
the enumerated type being stored in the variable 
Icmd. (The second part of the input line would be 
ignored until another read command was issued by 
the program—presumably this would take place in 
the pcarry procedure.) 


119 


This is an example of the concept of conversion 
of representations. It is worth talking about this a 
little further to give you an idea of why our “best of 
all implementations” scenario is usually not the 
reality. 

What kind of values are stored in a variable like 
Icmd? In the guts of the actual Pascal program, they 
are whole numbers: 1, 2, 3, and so on. The actual 
numbers are not guaranteed to be any particular 
values. The definition of Pascal only requires that 
the numbers assigned be consecutive. It is true that 
90% of all Pascal compilers will assign values be- 
ginning with 0. I would venture to say 100%, but 
then I am not personally acquainted with every 
Pascal compiler ever written! The correspondence 
between the identifiers in the declaration and the 
numbers chosen to represent these identifiers is 
maintained by the Pascal compiler. So whenever 
your program refers to one of the identifiers in the 
enumerated type, the right numeric value is sub- 
stituted for it in the translated program. 

Once a Pascal program has been compiled, the 
identifiers of an enumerated type are forgotten. 
They have all been assigned their internal numeric 
values. This is fine as long as you never wish the 
program to input or output a value of any enumer- 
ated type. Should you wish to do so, you are faced 
with the following fact: the identifiers (as they ap- 
pear in the declaration of the enumerated type) are 
character strings, but the internal values corre- 
sponding to those identifiers are numbers. For 
example, the cmds enumerated type might be as- 
signed internal values as follows: 


External (identifier) 


carry 
drop 

help 

light 
inventory 
take 

tally 

push 

dig 

look 

open 


Internal (number) 


SCOONODOUFWNHKH OC 


— 


120 


External (identifier) Internal (number) 


unlock 11 
eat 12 
nocmd 13 


The question becomes how do you “compute” 
a variable whose type is a user-defined enumerated 
type? In particular, how do you input such a value in 
response to a user’s command that is typed as a 
character string. The answer is basically pretty 
boring. You input the external representation of 
such a value and convert that value to its internal 
form yourself. This requires writing explicit Pascal 
code to do the job. In Adventure 2 the procedure 
cmdlookup performs this task. 

The first piece in solving the puzzle of enu- 
merated type input is an array of strings. The array 
must be indexed by the enumerated type. The 
strings in the array are the identifiers used in the 
declaration of the enumerated type. Thus for cmds, 
you have the array cmdnames, mentioned above. 
For example, cmdnames{inventory] := inven- 
tory ; The array cmdnames is set up in the proce- 
dure initialize. This is similar to the example of 
nicknames that I gave earlier. 

How do you use the string array? You use it to 
match a string typed by the user to one of the 
identifiers in the cmds type. This is the lookup 
aspect of cmdlookup. 

Actually, the strings read by cmdlookup may 
consist of more than just the command name itself. 
This is true because many command verbs take 
objects: carry keys, drop treasure, light lamp, eat 
crow, and so on. Also, the player may perversely 
type anything at all that forms a legal string: “Take 
me to your leader,” or “Come with me to the Cas- 
bah.” cmdlookup must first dissect the input string 
into two parts, which I have called head and tail. So 
cmdil-okup first reads the player’s response to the 
prompt into the string variable command. It dis- 
sects Command into two pieces by locating the first 
blank in the command string (if any). The part of the 
command preceding the first blank is stored in the 
variable head. The part of the command after the 
first blank (but not including it) is stored in the 
variable tail. 


The variable head will be used in the com- 
mand lookup process. The variable tail is used by 
the various command support procedures to deter- 
mine the details of specific commands. For exam- 
ple, if head is the command carry, tail will be used 
to determine what, if anything, will be carried. 

Figure 13-2 illustrates the picking apart of 
command. There is a “bug” in this code. Can you 
spot it? Can you fix it? 

Searching an Array Using the Linear 
Search. Searching is a technique used in many 
different computer programs. More often than not, 
it is a list that is searched. This is the case in 
Adventure 2. There are many kinds of search 
techniques, but I shall concentrate on one of the 


command ~~~¥P ‘carry A key” 
p:=pos (‘A’, command); 


command —~ 8 Carty A key” 


ws 


head := copy (command, 1, p-1); 


simplest, the linear search. 

The general technique of linear search as- 
sumes the existence of a linearly ordered collection 
of items. In this case the collection is the list of 
strings represented by the array cmdnames. 

Any collection of “things” organized in a lineup 
of some kind may be subjected to linear search. 
Here are some examples from real life: 


w A pile of magazines on a coffee table. 

w A bin of watercolor paintings on sale at the art 
gallery. 

m A pile of essays waiting to be graded by an 
English teacher. 

g A collection of recipes on 3x5 cards. 


tail := copy (command, p+1, length (command)-—p); 


command —~~w ‘carry A key” 


~~ length (command)—p 
1 J 


head —~e “carry” 


tail —w “key” 


Fig. 13-2. The dissection of a command into head and tail. 


121 


@ The want ads in your local newspaper. 


In all these examples, if you were searching for 
a specific item such as last month’s issue of your 
favorite computer magazine, a painting by a specific 
artist, an essay by your favorite pupil, a recipe for 
shrimp jambalaya, or an ad for a used printer for 
your computer, you would be apt to start at the 
beginning and search through the collection one 
item at atime. You would look at each item to see if 
it was the one you wanted, and continue until you 
either found that item or came to the end of the 
collection. 

In some cases, you might take advantage of 
extraneous information to speed up your speech. 
For example, you might remember the color of the 
cover of the magazine and limit your search to 
magazines whose covers were of that color. You 
might look for a painting that had the recognizable 
style of the specific artist you were interested in. 
You might look for certain key words in the news- 
paper ad, such as printer or computer. 

Because you are human, you have sophisti- 
cated pattern matching abilities with which a com- 
puter cannot yet compete. A computer, searching a 
list of items, is not able to use such cues in most 
cases. It has to take each item in turn to see 
whether or not it is the one being sought. This is 
always true when the list being searched has no 
other structure than that of a list. Our arrays are 
simply big unsorted piles—like a collection of 
twenty years’ worth of LIFE magazines, well shuf- 
fled from use. 

Here’s how to search: 


1. First ask whether or not there are any more 
items left in the pile you are searching. If the 
answer is yes, continue the search by doing step 
2. If the answer is no, stop the search. 

2. Isthe next item in the pile the item for which you 
are looking? If it is, you have succeeded, so quit. 
If it is not, do step 3. 

3. Put aside the item you just examined and re- 
jected. Continue the search from step 1. 


The following easy-to-remember names can 


122 


be given to the three steps in the above procedure: 
1. TEST, 2. COMPARE, and 3. LOOP. I shall refer 
to these steps in the following discussion. 

Arrays of Strings and String Compari- 
son. To be able to perform searches of string ar- 
rays, you must be able to compare one string to 
another. For cmdlookup, you must be able to com- 
pare strings in cmdnames with the string in head. 
UCSD Pascal allows you to make comparisons of 
string variables for equality and inequality: 


S1 = S2 is true <=> S1 and S2 contain the same 
character string. 


S1 <> $2 is true<=>S1 and S2 differ in at least 
one character position. 


It is also possible to compare two character 
strings lexicographically. This is a fancy word for “in 
dictionary order.” The mathematical comparison 
symbols < and > are used to mean the following: 


S1 < S2is true <=> The string in S1 would come 
before the string in S2, if 
both strings were in the 
dictionary. 


S1 > S2 is true <=> The string in S1 would come 
after the string in S1, if both 
strings were in the dictio- 
nary. 


The search in cmdlookup only uses the comparison 
for inequality. 

The test for the completion of a linear search, 
“Are we out of items to consider?” is not conceptu- 
ally part of the search itself. It seems like unwanted 
extra baggage. You will soon see that it really is. 

Suppose you knew ahead of time that what you 
were searching for definitely was one of the items in 
the collection being searched; or to put it another 
way, suppose you knew at the start that a successful 
search was guaranteed. Then the TEST part of the 
procedure would be superfluous. You might think 
the whole search would be superfluous! Leaving 
that issue aside for a moment, let’s see if we can 


“Ordinary” 
array 
locations 


} Extra location at 

the end of the 
array to hold the 
sentinel value. 


think of a way to guarantee that all your linear 
searches have happy endings. 

Figure 13-3 gives the basic idea—an extra 
location in the search collection. Now why would 
you want to increase the number of items to be 
searched through? To guarantee success, of 
course. You will always use the extra location to 
store a “copy” of the item you are looking for. Then 
if that item turns out not to be in the collection 
proper, you will still find it in the extra location at 
the end. You won’t have to worry about testing to 
determine whether or not any items are left. At the 
very worst, you will find what you are looking for 
just before you run out of items to consider. 

The extra item added to the collection is 
known as a sentinel. It “stands guard” against the 
possibility of failure. 

You now have a slightly different problem to 
solve with your search. There are now two possible 
ways to “succeed.” 


1. Find the sentinel. 
2. Find the item you are searching for before 
reaching the sentinel. 


Fig. 13-3. A picture of an array set up for 
the sentinel search technique. 


In the first case, even though you succeed in 
one sense, you fail in the larger sense. Case 2 could 
be dubbed “real success.” How do you know if you 
“really” succeed? Simple! After you succeed (which 
you know you will, because you have a sentinel), 
you check to see if you are at the sentinel location. If 
you are not, then you really did succeed. If you are 
at the sentinel you were only helped over the finish 
line. Real success awaits in some future search. 

You may have been wondering why nocmd 
was one of the identifiers in the enumerated type 
cmds. After all, you probably couldn’t imagine 
typing nocmd as a command. It is there as a place 
holder for the sentinel that will be used in the linear 
search in cmdlookup. The cmdlookup function 
guarantees the success of the linear search by 
storing the string contained in head in cndnames 
[nocmd]: 


cmdnames[nocmd] := head; 
The search is a very simple while loop: 


WHILE head <> cmdnames[Icmd] DO 


123 


Icmd := succ (Icmd) 
(* END DO *); 


When this loop terminates, the value stored in 
Icmd will either be the value of cmds correspond- 
ing to the command the player typed or the value 
nocmd. 

The succ Function. In order to cause Icmd (a 
variable of type cmds) to take on the “next” value of 
its enumerated type, the intrinsic function Succ is 
provided. Its action corresponds to picking the next 
identifier from the list of identifiers given in the 
declaration of the enumerated type. It is analogous 


124 


to saying x := x + 1 for a variable x of the integer 
type. For example, 


succ (carry) = drop 
succ (tally) = push 
succ (eat) = nocmd 
succ (open) = unlock 


The succ function is undefined for the last 
element in an enumerated type. For cmds, succ 
(ncmd) is undefined. An attempt to use succ 
(nocmd) in an expression will cause an error when 
that expression is evaluated at run time. 


Carry and Drop: Pascal Sets 


In the last chapter I discussed the overall im- 
plementation of command processing in Adventure 
2. In this chapter I will look more closely at two of 
the commands themselves: carry and drop. Both of 
these commands make use of the Pascal concept of a 
set. 

The Pascal language allows for variables of 
type set. Very few languages allow the programmer 
to create and manipulate sets. This is somewhat 
surprising, because the idea of a set is a common 
one in the real world and is often used in programs. I 
shall discuss the concept briefly below and show 
how it is simulated in languages that do not support 
it directly. 


WHAT ARE SETS AND HOW CAN THEY BE USED? 


The concept of the set may be given a very 
precise mathematical definition. I will not subject 
you to sucha treatment, however. Let us define set 
by giving some synonyms: collection, group, and 
aggregate, etc. Try to understand set by consider- 
ing some examples: 


g A stamp collection. 
w A set of tools. 

g@ A set of silverware. 
g A collection of books. 


Any group of similar items may be considered to be 
a set. 

Sets occur in many natural situations. In our 
adventures, the collection of objects that the player 
is carrying at any given time is a set. It turns out to 
be very easy to represent this in Pascal. In other 
languages, it is not so easy. Let us briefly consider 
how a set can be represented in BASIC. 


Sets in BASIC 


In adventure games, the player can pick up and 
carry various objects. Consider the problem of 
keeping track of which objects are being carried. 
First of all, you need to represent the objects them- 
selves. In Pascal, this is best done by an enumer- 
ated type: 


object = (lamp, treasure, key, sandwich, bot- 
tle, shovel, noobj); 


125 


In BASIC, there is no best or natural way to 
represent the objects. One straightforward way is 
to choose numeric variables whose values are 
similar to those that might be assigned to the iden- 
tifiers in the Pascal enumerated type: 


LA = 1 
TR =2 
KE = 3 
SA = 4 
BO = 5 
SH = 6 
NO = 7 


Most BASIC interpreters will allow you to 
spell out longer variable names, even though only 
the first two characters are significant: 


LAMP = 1 
TREASURE = 2 
KEY = 3 

SANDWICH 


an | 


Now, how do you represent the set of objects 
that the player is carrying? A simple way is to use an 
array: 


DIM CARRY(7) 


Each entry in the array corresponds to one 
object. The value of the array entry represents 
whether or not that object is being carried. The 
value of 1 means that the object is being carried, the 
value of 0 means that the object is not being carried. 
The collection of values stored in the array repre- 
sents the set of items being carried by the player. 

For example, if CARRY(6) = 0, the player is 
not carrying the shovel. If CARRY(1) = 1, the 
player is carrying the lamp. 

Storing several values, each of which is either 
0 or 1, in an array is wasteful of memory. Another 
technique for representing a set using Os and 1s is to 
use a bit string instead of an array. A bit string is 


126 


nothing more than an ordinary whole number 
thought of in its binary form. The Os and 1s in the 
binary numeral for the number may be used in the 
same way the Os and Is in the array are used. 

In this simple example, you can get by with a 
number between 0 and 63 to represent any possible 
collection of the 6 objects. The “object” noobj is not 
something the player can carry. It is used in a 
fashion similar to that of nocmd in the last chapter. 
Thus, it can be ignored when you consider the set 
representation problem. 

Each object is assigned to a particular power of 
two between 1 and 32: 


LAMP <===> 1 = 2 to the Oth power 

TREASURE <===> 2 = 2 to the 1st 
power 

KEY <===> 4=2 to the 2nd power 

SANDWICH <===> 8 = 2 to the 3rd 
power 

BOTTLE <===>16 =2to the 4th power 

SHOVEL <===> 32 = 2 to the 5th 
power 


Then, each number between 0 and 63 can be 
represented by adding together some collection of 
these powers of two. The number obtained by 
adding the values assigned to a given collection 
then represents the situation in which the player is 
carrying the objects that correspond to the powers 
of two chosen. For example, if the player is carrying 
the key, sandwich, and shovel, the corresponding 
number would be 4 + 8 + 32 or 44. If, the player is 
carrying the lamp and the bottle, the corresponding 
number would be 1 + 16 or 17. If the player is 
carrying nothing, this is represented by the number 
0. If the player is carrying all six objects, this is 
represented by the number 1+2+4+8+16+32 or 
63. 

Using bit strings in BASIC would require spe- 
cial code to calculate and interpret the various num- 
bers. All this is very cumbersome and I will not 
carry our treatment any further. In fact, UCSD 
Pascal (and many other Pascal systems as well) 
uses bit strings to represent its set variables, but it 
handles all the interpretation of them automatically. 


The programmer can think in terms of the set itself 
and in terms of concepts naturally allied to sets. 

In Adventure 2, you find the following type 
declarations: 


objects = (lamp, treasure, key, sandwich, 
bottle, shovel, noobj); 


collection = SET OF objects; 


This is so “natural,” so close to the way we 
think about the real situation that it takes some time 
for us to realize that this is programming language 
code. A collection is exactly that —a set of objects. 
The enumerated type objects represents the ob- 
jects in terms of their names, not as numbers or 
powers of two. Set variables may be thought of 
directly in terms of the set concept. The program- 
mer may ignore all problems of representation. 


Set Variables Used In Adventure 2 


There are two collection variables declared in 
Adventure 2. One is a scalar variable called stash. 
stash is the set of objects that the player is carrying 
at any given time during the game. The other vari- 
able is an array of sets called whatshere. This array 
is indexed by the enumerated type rooms. An entry 
in this array corresponds to the set of objects that 
happens to exist in a given adventure location at any 
time. Thus, whatshere[start] represents the ob- 
jects that are at the start location. whatshere[m19] 
represents the objects that are at location m19 (the 
nineteenth room in the maze). 


The Use of Set Variables: pcarry and pdrop 

As the adventurer moves around, he will issue 
commands to carry and drop objects. The adventure 
guide notes which objects are at which locations as 
those locations are visited. This cues the adven- 
turer that carrying some object is possible. For 
example, at the start location the guide says: 


There is a lamp here. 


The adventurer may in turn say: 


carry lamp 


If the player wishes to leave an object some- 
where, he or she can use the command drop. Of 
course, you cannot drop something you are not 
carrying. 

The pcarry and pdrop procedures contain the 
Pascal code that manage the carry and drop com- 
mands. They make use of Pascal capabilities re- 
garding set variables. In addition, the initialize pro- 
cedure determines the original values for all the set 
variables in the program. In particular, the variable 
stash must initially be the empty set, that is, the 
collection of no objects at all. This initialization is 
accomplished by the Pascal assignment: 


stash := [ ]; 


In general, set constants are represented by lists of 
items inside square brackets: 


setvar := [item1, item2, item3]; 


In addition to simple items, you may also use the 
Pascal subrange notation in order to include several 
items in a set constant. For example: suppose 
mazerooms was a variable of type SET OF rooms. 
Then the assignment: 


mazerooms := [m1 . . m19]; 


would assign to mazerooms the set of rooms con- 
sisting of m1, M2, M3, m4, m5, m6, m7, m8, m9, 
m10, m11, m12, m13, m14, m15, m16, m17, 
m18, and m19. The subrange notation m1 ..m19is 
more compact for this purpose than listing all the 
individual rooms themselves. 

In addition to setting stash to [ ], initialize also 
determines where each object is by assigning ob- 
jects to various whatshere entries: 


whatshere[start] := [lamp]; 
whatshere[coldroom] := [shovel]; 
whatshere[narrow4] := _ [key]; 
whatshere[deadend] := [sandwich]; 
whatshere[mudroom] := __ [bottle]; 


127 


Notice that there is no assignment: 
whatshere[m19] := [treasure]; 


This is because the player cannot see the treasure 
until certain problems have been solved. I will dis- 
cuss this further in the next chapter. 

The pcarry Procedure. The pcarry proce- 
dure makes use of two subsidiary functions, 
objlookup and ckobject. When the player types a 
carry command, there is an object named after the 
verb carry: carry key, carry treasure, carry sand- 
wich, and so on. The Pascal code must analyze the 
command string. It must determine whether or not 
the string stored in tail (see the preceding chapter 
for details of how tail is determined) corresponds to 
one of the objects in the game. This is accomplished 
by another linear search with the sentinel al- 
gorithm, just like the one used by cmdlookup. See 
the last chapter for a detailed discussion of this 
algorithm. 

The objlookup function uses an array Obj- 
namesfobjects], which is to objects what the array 
cmdnames was to commands. That is, objnames 
contains strings of all the names of objects in the 
game. These strings are used in comparisons with 
the variable tail. The code in objlookup is strictly 
analogous to that in cmdlookup. The value re- 
turned by objlookup is a value of the enumerated 
type objects. This includes the possible value 
noobj. If objlookup returns noobj, it means that the 
string in tail is not the name of one of the objects in 
the game. This can happen, for example, if the 
player types the command carry beans or carry 
knife. 

The function ckobject simply checks to see 
whether or not the object requested by the player is 
in the current location. This is accomplished using 
the Pascal set operator IN. 

In general, if S is a set variable and O is a 
variable whose value is one of the possible “items” 
in the set, the Pascal expression 


OINS 


is either true or false depending on whether or not 


128 


the particular item represented by the value of 0 
happens to one of the items stored in S. So for 
example, 


key IN [shovel, lamp] is FALSE 

treasure IN [sandwich, shovel, treasure] is 
TRUE 

noobj IN [lamp . . shovel] is FALSE 


ckobject uses the expression 
it IN whatshere[location] 


where, it is the particular value of type objects 
determined by objlookup. Because the value noobj 
can never be one of the values in any of the 
whatshere entries, the ckobject expression will 
always be false if the player requests a nonexistent 
object. 

If ckobject (it) turns out to be false, pcarry 
informs the player 


| don’t see any “it” here 


where, of course, it is filled in with the specific 
object the player requested. This can either mean 
that the object is not at that particular location (even 
though it does exist) or that the object simply does 
not exist. The player can’t tell which is the case 
merely by reading the message. That is, if the 
player says 


carry baloney 
and the guide replies: 
| don’t see any baloney here. 


the player cannot tell whether or not any baloney 
exists in the game. All that is revealed is that there 
is no baloney at this particular location. 

If, on the other hand, ckobject (it) is true, the 
object not only exists, but is present at the player’s 
current location. This means that the carry com- 
mand should be “carried out.” This involves 


changing the value of two different set variables; 
stash and whatshere[location]. For this purpose, 
there are two Pascal operators available for adding 
an object to a set or for removing an object from a 
set: + and —. The specific Pascal statements used 
are stash := stash + [it];, which adds the object it to 
the collection represented by stash, and what- 
shere[location] := whatshere[location] — [lit];, 
which removes the object it from the collection 
represented by whatshere[location]. These as- 
signments reflect the real situation being modeled: 
the player is adding the object to the collection 
being carried, and the same object is being removed 
from the location being visited. 

The pdrop Procedure. The pdrop proce- 
dure is quite similar in spirit to pcarry. It calls 
objlookup to determine the object requested. It 
checks to see whether or not the player is carrying 


that object: 
IF NOT (it IN stash) 


If the player is not carrying the object, the guide 
replies: 


You are not carrying any ‘it.’ 


Otherwise, the object is removed from the player’s 
collection: 


stash := stash — [it]; 


and added to the collection of objects at the current 
location: 


whatshere[location]: = whatshere[location] 
+ [it]; 


129 


Problems in Adventure 2 


Adventure 2 contains the first examples of real 
problems. In this chapter, I will present techniques 
for representing problems and handling their solu- 
tions within the play of the game. 

In Chapter 3 I began our discussion of adven- 
ture game problems. There I covered general 
characteristics of problems from the players’ point 
of view. I talked about clues and hints. I delved into 
the topics of problem difficulty and repeatability. I 
discussed the use of logic in problem solution and 
the element of surprise. 

Now is the time to think about problems more 
from the perspective of implementation. What are 
the key features of problems and how do you “map” 
them into Pascal code? 


EVENTS 


Many problems revolve around the simple 
concept of an event. An event is just that— 
something that may or may not occur. An event may 
happen once or it may represent a situation that may 
flip-flop. For example, the act of carrying some- 


130 


thing is an event. The act of dropping the same thing 
is the opposite of the same event, that is, the act of 
carrying has not occurred if the act of dropping has 
occurred. This is an example of an event that re- 
flects the current situation. 

Some other examples of events are 


@ The player has not found the treasure. 

@ The player is or is not in a specific location. 
@ The player has or has not dug a hole. 

@ The ogre has or has not been awakened. 

@ The ogre has or has not eaten the player. 


and so on. The occurrence of an event may be 
recorded by setting a Boolean variable to the value 
true. The same variable may have the value false if 
the event has not occurred. This approach can be 
used for the events that are really situations as 
well. One side of the situation (the player is carry- 
ing the treasure) may be represented by the value 
true, while the other side (the player is not carrying 
the treasure) may be represented by the value 


false. This is basically the technique used in Ad- 
venture 1. 


BOOLEAN EXPRESSIONS AND EVENTS 


If you can use Boolean variables to represent 
simple events, what kind of code can you use to 
compute values for those variables? The answer is 
that you can use Boolean expressions. Boolean ex- 
pressions are Pascal expressions whose values can 
be reduced to either true or false. Boolean expres- 
sions may also be used to represent more compli- 
cated events that depend on more than one situa- 
tion. 


Numeric Relationships 


Two numbers may be compared using any of 
several relational operators: 


turns = 0 
turns <>0 
turns < 25 
turns > 75 
turns <= 100 
turns >= 50 


V 


VAVAA I 


In all these examples, the variable turn is compared 
to some number. Other examples might compare 
the values of two variables. In all such examples, 
the result of the comparison is true or false: either 
the two values being compared stand in the re- 
lationship used or they do not. 

Numeric relationships may also be used to 
represent events or to “compute the value of an 
event.” For example, the relationship 


turns >= 75 


could represent the situation in which the player’s 
lamp runs out of energy or the player runs out of 
time. 


Set Relationships: Set Membership 

The Pascal operator IN yields a result which is 
either true or false. IN is a binary operator (not 
symmetric). The first operand of IN is a possible set 


member, one of the elements that could be in the 
set represented by a given set variable. The second 
operand of IN is a specific set variable. The result of 
the set membership relationship: O IN S is true if 
the element represented by the value of O is an 
element of the set that is the present value of S. 

I have already discussed sets extensively in 
the previous chapter. You have seen how the set 
membership relationship may be used to represent 
the event of carrying or not carrying an object, for 
example, key IN stash or treasure IN whats- 
here[start]. 


Equality for Enumerated Types 


A special case of the = (equals) relationship 
occurs for variables whose type is an enumerated 
type. An identifier of the enumerated type may be 
used as one of the operands of an = operator. The 
other operand will be a variable of the enumerated 
type. This is used frequently in adventure games in 
Pascal; for examples location = start, it = trea- 
sure, and so on. Again, these relationships repre- 
sent events in the ways that have just been discus- 
sed. 


Building Boolean Expressions: Boolean Operators 


The relationships just explained, plus simple 
Boolean variables, are the building materials of 
Boolean expressions. The nails and fasteners that 
stick these materials together are called Boolean 
operators. The Boolean operators are AND, OR, 
and NOT. 

The Boolean operators are used ina way that is 
similar to the way the same words, (and, or, and 
not) are used in making declarative statements in 
ordinary English. In such a context, the words and, 
or, and not are usually referred to as logical connec- 
tives. This is the grammatical equivalent of the 
Pascal term Boolean operators. 

Think about the declarative statements 


It is raining. 

It is cold. 

I am a millionaire. 

The stock market went up today. 
Apples are red. 


131 


A Buick is an automobile. 


and so on. Such statements are either true or false. 
In ordinary speech, people compose more complex 
declarative statements by combining such simple 
statements using and, or, and not: 


It is raining and it is cold. 

It is snowing or it is hot. 

It is not raining. 

It is sunny and either the stock market went up 
today or the New York Yankees lost a baseball game 
today. 


The truth or falsity of such statements depends 
on the truth or falsity of the simple statements 
involved and the particular connectives used. For 
example, the statement 


I weigh 175 pounds and I weigh 200 pounds 
is always false. But, the statement: 
I weigh 175 pounds or I weigh 200 pounds 


may sometimes be true and other times be false. 
The use of ov instead of and changes the way in 
which the truth or falsity of a statement is deter- 
mined. 

In Pascal, the relationships and Boolean vari- 
ables are the analogues of simple declarative 
statements. They may be combined to form expres- 
sions, which are the analogues of more complex 
statements: 


(turns > 0) AND (location = start) 

(treasure IN stash) AND (NOT eaten) 

NOT (treasure IN whatshere[location]) 
Notice the difference in syntax. Pascal does not 
read exactly like English. The two major things to 


watch for are 


@ The use of parentheses. 
@ The prefixing of NOT. 


In general, it is a good idea to put parentheses 


132 


around each operand of an AND, OR, or NOT 
operator. There are times when they may be omit- 
ted, but their proper use never hurts. For example, 
the expression 


turns > 0 AND location = start 


will cause Pascal to issue a syntax error message, 
while 


(turns > 0) AND (location = start) 


is correct. 


The technical details regarding this matter in- 
volve the concept of operator procedure. That is a 
topic for a textbook dealing strictly with Pascal 
language rules and regulations. Consult your fa- 
vorite manual of this type for those details. Mean- 
while, if you imitate the code in the sample adven- 
tures and use parentheses liberally, you should 
avoid most problems with syntax errors. 


PROBLEMS IN ADVENTURE 2 


To conclude this chapter, I discuss the specific 
problems of Adventure 2 and their Pascal encoding. 
In each case, I shall describe the general problem in 
English. The description is from the point of view of 
the adventurer. Then I give a set of preconditions 
and postconditions. The preconditions describe 
what must take place in order for the player to be 
able to solve the problem. This includes events that 
must take place and commands that must then be 
issued. These are generally described in Pascal, 
with English comments when deemed necessary. 


Problem 1 


You must go to the island in order to read the 
message. 


Precondition 
location = island 


The preconditions in this problem are fulfilled 
merely by the player arriving at the island. The 


pisland procedure is not invoked unless the pre- 
condition is met. 


Postcondition 
readmsg = TRUE 
The code in pisland causes this to occur. 


Problem 2 


You must find and carry the shovel in order to 
later be able to dig for the treasure. 


Precondition 


location = coldroom 
command issued is “carry shovel” 


Postcondition 
(shovel IN stash) = TRUE 


The support procedure pcarry, of course, guaran- 
tees that this is the case. 


Problem 3 
You must dig three times in order to find the 
treasure chest. 


Precondition 


(location = m19) AND (shovel IN stash) 
AND readmsg 
command is “dig” 


Postcondition 


The integer variable hasdug is one larger than 
before. 


Each time the preconditions of this problem are met 
(including the issuance of the dig command, the 
value of hasdug is increased by 1. 


Problem 4 
If you dig four times, you fall into the flames. 


Precondition 
hasdug = 4 
Postcondition 


cooked = TRUE 


If you keep on digging, you eventually dig a hole 
into the flames room. You get a warning after the 
third time you dig. By that time, you have been told 
that a chest has been uncovered. If you are greedy 
and keep digging, you get “toasted.” 


Problem 5 


You must find the key and carry it in order to 
open the treasure chest. 


Precondition 


location = narrow4 
command is “carry key” 


If you arrive at the treasure location without the 
key, you will be unable to open the chest when it is 
uncovered. Of course, you can always go back and 
find the key after you have dug up the treasure, but 
this may take more turns. 


Problem 6 


You must push the treasure through the crack 
between narrow1 and the vestibule. 


Precondition 
location = narrow1 
treasure IN whatshere[narrow1 ] 
command is “push treasure” 
Postcondition 
treasure IN whatshere[vestibule] 
The treasure is too heavy to carry up the ladder. 
The only other way to get it back to the start is to 


get it into the vestibule by means of this command. 
This is the most difficult problem in Adventure 2. 


133 


There is a hint about what to do. If you visit the treasure IN whatshere[vestibule] 


location deadend, you will be able to read the location = vestibule 
message “Good things go through small places.” command sequence is “carry treasure” and 
then “up” 
Problem 7 
You must carry the treasure to the start loca- Posteoudition 
tion and drop it there in order to win. 
Precondition treasure IN stash 


134 


Other Techniques Used in Adventure 2 


In this chapter I discuss miscellaneous techniques 
used in Adventure 2. The approach is similar to that 
of Chapter 10. You should read the explanations 
here and then study the relevant sections of code in 
the listing of Adventure 2. This is a chapter to be 
dipped into at random and over and over again. 


COUNTING TURNS 


The counter turns keeps track of the number of 
turns a player has taken. If you look through the 
code, you will see that it is only changed in one 
place, namely in the whichway function. What this 
means is that the player takes one turn for each 
direction or travel command given. Other com- 
mands, suchas, carry, drop, eat, or dig do not count 
as turns. This is a somewhat arbitrary decision. It 
gives the player slightly more time to play. On the 
other hand, it is not the most liberal policy of 
counting I could have adopted. I could have counted 
only those travel commands that succeed. A good 
exercise is to change the code to use this more 
liberal rule. See if you can figure out how to do it. If 


you like that rule better, then put it in! 


DISPLAYING THE CONTENTS OF A SET 


The procedures pinvent and showobjects 
both cause the contents of a set variable to be 
printed out. This involves the use of the string array 
objnames, which was discussed in Chapter 13. 

The technique is simple: the IN operator is 
used to check for the presence or absence of each 
possible item. This can be done in a FOR loop: 


FOR lobj := lamp TO noobj DO 


If an object is present in the set, its identifier is 
printed out using the objnames array: 


write (objnames[lobj]); 


The pinvent procedure goes through the set vari- 
able stash in this fashion. The showobjects proce- 
dure goes through the variable whatshere[loca- 


135 


tion]. The latter clearly depends on where the 
player happens to be at a given time. 


SIMPLIFICATION OF TRAVEL 


Adventure 2 contains a new procedure called 
travel, which simplifies the changing of location 
during the play of the game. It replaces most of the 
case statements that were used in Adventure 1 for 
that purpose. 

The travel procedure takes six parameters of 
type rooms: 


PROCEDURE travel ( 
nloc, 
sloc, 
eloc, 
wloc, 
uloc, 
dloc: rooms); 


A typical call of travel might look like 


travel (deadend, batscave, nowhere, nar- 
row2, nowhere, maze); 


which means that from the current location (which 
happens to be the location steam), the possible 
destinations are deadend going north, batscave 
going south, nowhere going east, narrow2 going 
west, nowhere going up, and maze going down. 
The travel procedure uses a nested procedure 
called newloc. The newloc procedure checks the 
requested destination. If it is nowhere, it prints the 
message “There is no way to go in that direction.” If 
it is not nowhere, it changes the value of the vari- 
able location and sets the Boolean variable chgloc 
to true—I’ll say more about chgloc shortly. 

The travel procedure uses a case statement 
that resembles those used in Adventure 1. This 
case statement uses a call to newloc for each case 
label: 


CASE whichway OF 


n: newloc (nloc); 
s: newloc (sloc); 


136 


: newloc (eloc); 
: newloc (wloc); 
: newloc (uloc); 
: newloc (dloc); 


aczo 


END (* CASE whichway OF +); 


The travel procedure is a more compact way to 
handle directional commands. It removes the need 
for many of the location procedures that were used 
in Adventure 1. The rule is that if a location has no 
special case code, there does not need to be a 
corresponding location procedure for it. A location 
such as ladder still needs to have a separate case 
statement to handle travel. This is true because the 
ability to go in the up direction depends on whether 
or not the player is carrying the treasure. This 
conditional travel is not handled by the code 


u: newloc (uloc); 


in travel. 
The case statement 


CASE location OF 


in the main program block of Adventure 2 now 
invokes travel in most cases (24 out of 33). This 
makes the program both smaller and easier to un- 
derstand. 

In Adventure 1, the description of a location is 
repeated as long as the player stays there. This can 
get annoying, especially if the description is long. 
Adventure 2 solves this problem. The description 
of a location is given only when the player enters 
the room. If the player then issues commands and 
stays in the room, the description will not be dis- 
played again. This is accomplished by using the 
Boolean variable chgloc; chgloc is set to false by 
the procedures noway and docommand. It is set to 
true by newloc and by various location procedures 
whenever the players’ location has actually 
changed. The show procedure checks the variable 
chgloc to determine whether or not to print the 
description. 

The player may explicitly request that the de- 


scription be repeated by giving the command look. 
The plook procedure saves the current value of 
chgloc, and temporarily sets chgloc to true. It then 
calls show for the current location. After show 
prints the description, plook sets chgloc back to its 
old value (which may be either true or false). 


THE USE OF FILE VARIABLES 


Most of the descriptions of locations used in 
Adventure 2 come from a disk file. This saves space 
in the program allowing more locations and more 
problems. It also causes the printing of descriptions 
to be slower. However, this seems to be a reason- 
able tradeoff. 

Placing descriptions in a file requires the use 
of a subsidiary program that I call MAKEDESC 
(short for MAKE DESCriptions). MAKEDESC 
reads a text file of descriptions and creates a 
database of descriptions for use by an adventure. 
The database consists of two files, an index file and 
a descriptions file. 

The construction and use of MAKEDESC are 
discussed in great detail in several chapters begin- 
ning with Chapter 18. Included in those chapters is 
information on how to use MAKEDESC in writing 
your own adventures, as well as technical discus- 
sions of MAKEDESC and the support code needed 
for using the output from MAKEDESC. 


SCORING YOUR ADVENTURES 


People play adventures for many reasons. 
They want to solve the problems posed, they want 
to find the treasures, and they want to “win.” The 
definition of win is usually “accumulate the highest 
possible score.” 

A player may gain points for a number of ac- 
complishments including 


@ Solving problems in the adventure. 

@ Visiting each location in the adventure (a certain 
number of points for each location visited). 

@ Visiting all locations in the adventure (bonus). 

@ Finding the treasure(s). 

mw Bringing the treasures back to a safe place. 


In addition to points awarded for positive ac- 


tions, there may be points subtracted from the total 
score for various other reasons: 


w Asking for help or hints during the play of the 
game. 
@ Not avoiding various dire actions including: 


Waking the ogre (or other monsters). 

Being killed and having to be reincarnated. 

In Adventure 2, I have devised a scoring func- 
tion that takes such various factors into account. 
Here’s the way I have it now: 


@ 5 points are awarded for each location visited. 

@ 140 points are scored for getting the treasure 
back to start. 

@ 50 points are deducted for being cooked; that is 
falling into the flames. 

@ 50 points are deducted for being eaten by the 
ogre. 

@ 25 points are deducted for waking the ogre. 


THE HELP COMMAND 


Adventure 2 contains a help command. If the 
player asks for help, the guide will respond by 
giving hints. If the player has already read the 
message on the island, the guide will tell the player 
that the treasure is in the maze. Otherwise, the 
guide will tell the player where hints may be 
found—namely, near the lake and in the alcove. 

The scoring rules in this adventure do not 
punish the player for asking for help. You might 
want to modify Adventure 2 so that a few points are 
deducted if the player ever asks for help. This 
would require another Boolean, variable (like 
cooked or quit), which would be false initially, but 
which would be set to true if the player asks for 
help. The score function may then examine this 
variable to see if points should be deducted or not. 


THE LAMP AND THE LIGHT COMMAND 

Adventure 2 requires that the player carry the 
lamp found at the start. The lamp must also be lit. 
Otherwise, the player falls into a pit and is killed 
after a few turns. One of the first problems the 


137 


player must solve is how to treat the lamp properly. 

The light command checks to make sure that 
the player has the lamp by using the set relationship 
IN: 


lamp IN stash 

If this is true, the lamp will be lit if the player says 
light lamp 

or, simply 
light 


The latter is a convenience for the player. Many 
adventure games have lamps that need to be lit. In 
other games, it is possible to use a variety of com- 
mands in order to light the lamp: lamp on, lamp, 
light, and so on. I have only provided two: light 
lamp and light. You might want to modify Adventure 
2 to add further commands that cause the lamp to be 
turned on. 

The cklamp procedure is provided to monitor 
the status of the lamp. It counts the number of turns 
the player spends underground with the lamp 
turned off. If this gets too large, the game is ended. 
It warns the player whenever the lamp isoff. It also 
heavy-handedly turns the lamp off automatically 
when the number of turns taken gets too large. This 
forces the player to finish up the game in a certain 
amount of time in order to win. 


THE DIG COMMAND 


When the player gets to the right maze loca- 
tion, it is time to dig for the treasure. Provided that 
the player has located and is carrying the shovel, 
and has read the message on the island, the com- 
mand to dig will be obeyed. However, it is not 
enough to simply dig once—you have to keep on 
digging. In fact, you must dig at least three times 
and no more than four times. Each time the dig 
command is given, a new message is conveyed to 


138 


the player. This is controlled by the variable has- 
dug, which counts the number of times the player 
has used the dig command. 


THE EAT COMMAND 


The eat command has been added to Adven- 
ture 2 strictly for fun. There is a sandwich that may 
be found. If the player is carrying the sandwich and 
says “eat,” the guide replies, “Oh, yummy!!” Other 
messages are given, depending on the circum- 
stances. However, the sandwich and the eat com- 
mand play no role in the scoring of Adventure 2. 


THE OGRE 


The procedures pogreroom and ogreaction 
handle the ogre character. The ogre is strictly bad 
news—nothing good is associated with the ogre. If 
the player visits the ogreroom (which must be done 
in order to get the maximum score), there is a 
chance that the ogre might wake up. If the ogre 
wakes up, there is a chance that he might catch and 
eat you. If you should ask to go down while you are 
in the ogreroom, you find out that you have stum- 
bled into the ogre’s cooking pit. All of these unde- 
sirable happenings cause you to lose points. 

Adventure 2 uses a very crude technique to 
decide whether any of these things occur. The 
number of turns is examined. If it is an even multi- 
ple of 3 (3, 6, 9, 12, and so on), the ogre is 
awakened. If the ogre is already awake and if the 
number of turns is an even multiple of 2, the player 
is eaten. The Pascal MOD function is used to de- 
termine these conditions: 


(turns MOD 3) = 0 
or 
(turns MOD 2) = 0 
This technique could be slightly improved by using 


a random choice. This would depend on the avail- 
ability of a random number generator. 


UCSD Pascal Development Techniques 


In Chapter 4 I discussed UCSD Pascal and some 
elementary techniques for its use. In this chapter, I 
wish to elaborate on those topics. In particular, I 
discuss how best to use your UCSD system in 
writing large adventure games and other Pascal 
programs. 


MANAGING YOUR FILES EFFECTIVELY 


Most personal computer systems equipped 
with UCSD Pascal use floppy disks as mass storage 
devices. Your files all “live” on various floppy 
disks. As the number and size of the files you have 
created grows, the problems of managing them 
grow accordingly. You have to worry about running 
out of space on a given floppy. You have to worry 
about sharing space with various UCSD system 
files. You have to worry about keeping track of 
various versions of your files. In short, you need to 
worry about more than just writing Pascal pro- 
grams. 


Organizing Your Source Files 
There are a number of problems associated 


with writing a large program in UCSD Pascal. I 
have already hinted at some of these. Now it is time 
to be more specific. 

UCSD Editor Limitations. The UCSD Pas- 
cal full screen text editor requires that the entire 
file being edited fit into memory. Unfortunately, 
Pascal programs tend to exceed this limit. That 
implies that there will be one of two results: 


@ You never write Pascal programs whose source 
files are bigger than what the UCSD editor can 
handle. 

@ You use techniques for splitting Pascal programs 
into more than one source file. 


The first alternative needs no comment. The 
programs in this book necessitate the use of the 
second alternative. 

Each implementation of the UCSD system im- 
poses its own limitation on the size of an editor file. 
The Apple Pascal limitation is approximately 
18,400 bytes, which is about 38 disk blocks. In 
order to leave room for expansion and for fixing 


139 


bugs, it is good to impose a smaller maximum size 
for each source file. 15,000 bytes seems like a 
reasonable choice: it is easy to remember and it 
allows for expansion. 
_ When you start writing an adventure game, 
you should monitor the size of the source file. In the 
UCSD editor, you discover the current size of your 
file by using the set environment command. This 
command gives you a screenful of information about 
the file you are editing. Among other things, it tells 
you the number of bytes used and the number of 
bytes remaining in the file. When the number of 
bytes used shows about 15,000 (or whatever limit 
you decide is reasonable on your system), it is time 
to start anew file. When to start a new file may also 
be based on the logical divisions of the program. In 
other words, it is silly to split a source file in the 
middle of a procedure just to adhere to the size 
limit. 

As an example of this technique, the following 
summary shows the source files used in Adventure 
2 and the sizes of each: 


File Name Size 

a2. data. text 2671 bytes 
a2.ul.text 5836 bytes 
a2.u2.text 10662 bytes 
a2.m2.text 4096 bytes 
a2.maze. text 2749 bytes 
a2.main.text 3499 bytes 


As you can see, none of these files comes close to 
our self-imposed limit. The division of the source 
code for Adventure 2 into these files was deter- 
mined more by the logical structure of the code than 
by size. However, in the case of a2.ul.text and 
a2.u2.text, size was the determining factor. These 
were originally a single file. They were split when 
the total size exceeded 15,000 bytes. The following 
summary shows the list of files again, this time with 
the parts of Adventure 2 included in each file shown 
instead of the file sizes: 


File Name Contents 


a2.data.text The const, type, and var sections 


140 


File Name Contents 


of the program, together with the 
simple procedure wipe. 


a2.ul.text | The introduction procedure through 
the ckobject function. 

a2.u2.text The pcarry procedure through 
the travel procedure. 

a2.m2.text The ogreaction procedure through 


the pogreroom procedure. 

The pmaze procedure. 

The ppit procedure through the end 
of the main program. 


a2.maze. text 
a2.main. text 


Compiling Multiple Source File Programs 


The UCSD Pascal compiler allows you to com- 
pile a single program that exists in more than a 
single source file. This is accomplished by the use 
of the include option. The include option tells the 
compiler to read more source code from another 
file. The form of the option is as follows: 


(*$lxyz.text«) 


This is similar to other UCSD Pascal compiler op- 
tions. Options are initiated by an open comment 
bracket, followed by a dollar sign: 


(*$ 


Then comes a single letter indicating which option 
is being used and any other information needed by 
the option itself. In the case of the include option, 
the other information is the name of the source file 
to be “included.” For example, in Adventure 2 at 
the end of the first source file, a2.data.text, the 
following sequence of include options appears: 


(*$la2.u1.text*) 
(*$la2.u2.text*) 
(*$la2.m2.text*) 
(*$la2.maze.text*) 
(*$la2.main.text*) 


The result is that the Pascal compiler reads through 
the source files a2.u1.text, a2.u2.text, a2.m2.text, 


a2.maze.text, and a2.main.text in that order, after 
it reads through the file a2.main.text. The net ef- 
fect is just as if all six source files had been con- 
tained in a single file. When you invoke the Pascal 
compiler to compile a2.data.text, the compiler will 
automatically find all the other files needed. You 
don’t need to learn any special commands in addi- 
tion to the include option itself. 


Giving the Compiler a Chance 


There is another option that should almost 
always be used when compiling large UCSD Pascal 
programs. That is the (*$S+*) option. This causes 
the compiler to go into “swapping” mode. Instead of 
having all of the code for the compiler in memory at 
the same time, various parts of the compiler code 
are swapped in and out. Only part of the compiler 
code is present in memory at any given time. The 
rest of the code stays on the floppy disk. 

Let us examine the consequences of either 
using or not using the swapping option. What is 
affected by the swapping option is the amount of 
memory that is available to the compiler for its 
symbol table. When the swapping option is not 
used, there is a limited amount of symbol table 
memory. This means that only a medium sized 
program can be compiled before this memory is 
exhausted. When the swapping option is used, the 
amount of symbol table memory increases dramati- 
cally and with it the size of program that may be 
compiled. It is possible to increase the amount of 
symbol table memory even more. This is done by 
using the “double swapping” option (*$S+++). 
This slows down the compiler, because it must do 
more juggling of code into and out of memory. It 
increases the symbol table space to its maximum 
amount and therefore provides for the largest pos- 
sible source program. 


UCSD SYSTEM TRICKS AND PITFALLS 


The UCSD system includes some commands 
that make dealings with your files and disks easier. 
These, however, should be used with caution. 


The Prefix #5 Trick 
The prefix command in the UCSD filer allows 


you to abbreviate file names when communicating 
with the UCSD system. The system automatically 
uses the prefix to decide which disk volume to 
search for a given file. If you set the prefix to #5, 
the system will read the volume name of the disk 
currently in the drive corresponding to #5. This 
means that if later you wish to change the disk in 
that drive, you must reenter the filer and reset the 
prefix to #5 again because the name of the physical 
disk has changed. Either that, or you have to type 
the volume name explicitly, the very task that the 
prefix command was designed to allow you to skip. 

Some implementations of UCSD Pascal, Apple 
Pascal in particular, allow you to use the following 
trick. First enter the filer program. Then open the 
door on the disk drive corresponding to unit #5. 
While the door is open, issue the prefix #5 com- 
mand. The disk will spin, aclicking or buzzing noise 
may eventually be heard, and the filer will eventu- 
ally respond by saying: 


Prefix is #5: 


This means that no matter what disk is in #5, the 
filer will use its name as the prefix. Thus, you don’t 
have to reissue the prefix command if you change 
the disk in #5. The system will automatically de- 
tect the change and use the new disk’s volume name 
as the prefix. 


The K(runch Command 


As you develop a large program, the disk con- 
taining its source files may need space mainte- 
nance. The available space on the disk tends to 
become fragmented because of the way the system 
moves files around during editing and compiling. 
The UCSD system provides the K(runch command 
to allow files to be rearranged. This command 
moves files around in order to create a single large 
block of unused space. It is a good idea to examine 
your disks occasionally and if necessary do a 
K(runch. The question is, when should you 
K(runch? 

There is no hard and fast rule about when to 
K(runch. However, sooner rather than later is the 
best idea. To start with, you should use the 


141 


TARR: 

FORMAT. CODE 
CMDNAMES. TEXT 
* UNUSED : 
TABCHI2. TEXT 
TABCHIS. TEXT 


» UNUSED : 
TABCH11. TEXT 
TARCHI4A. TEXT 
TABCHIS. TEXT 
«< UNUSED > 
TABCH14. TEXT 
TABCHI4E. TEXT 
FAKE. TEXT 
HDR. TEXT 

< UNUSED > 
11/11 files, 


2 24-Aug-as 


118 unused, 


6-Feb~82 & 
7-Feb-82 26 
=O) 
38 
42 
46 
66 
82 
116 
150 
180 
202 
212 
216 
220 


largest 


Code 
Text 


Text 
Text 


27~-Apr-8s 
27-Apr-83 


Text 
Text 
Text 


7 ~-Aug-BS 
24-Aug-83 


Text 
Text 
Text 
Text 


24--Aug-8S 
25-Aug-83 


am ud 


24-Aug-83 


60 in 


Fig. 17-1. A typical output from an E(xtended directory list command. 


E(xtended directory list command to get a picture 
of the current state of your disk. A typical output 
from such a command is shown in Fig. 17-1. 

Each line beginning with < UNUSED > ac- 
counts for a block of unused space on the disk. If the 
largest such blocks is not big enough to hold the 
largest file on the disk, you probably should 
K(runch. In the above display, the biggest available 
space is 60 blocks (this information is always given 
in the last line of the display: 11/11 files, 118 
unused, 60 in largest). The largest files on this 
disk are both 34 blocks in size. Either will fit com- 
fortably in the 60 block space. 

What are the consequences of not K(runching 
soon enough? It may become impossible to write 
out a new version of the file from the editor; or it 
may be impossible to perform a compile, because 
the compiler may run out of space for its output 
files. In the former case, there is a way out. In the 
latter case, you will have to abort the compile, do 
the K(runch, and recompile. So what should you do 
if the editor says 


ERROR: WRITING OUT THE FILE. 


PLEASE PRESS <SPACEBAR> TO 
CONTINUE 


142 


At this point, you should remove the offending disk 
from its drive and insert another that you know has 
enough room (it is a good idea to have at least one 
“scratch” disk available for this purpose). Write the 
file to this new disk using the write command. Then 
put the old disk back in, K(runch it, and use the filer 
to move the saved copy on the scratch disk back to 
your working disk. This is greatly facilitated if you 
have used the prefix #5 command as described 
earlier in this chapter. 


Losing Source Files Accidentally 


There are many ways that source files can get 
lost. Everyone hopes that such a disaster never 
happens to them. However, there are only two 
kinds of programmers: those who have lost their 
source code, and those who are going to. So it is 
wise to have some methods for combating lost 
source files. 

You should always have a copy of your pro- 
grams on paper. If you own a printer, this is easy 
enough to accomplish. If you don’t, you should save 
your pennies and get one, or you will wind up in the 
second category of programmers. When you make a 
listing of your program, write the date on it. Then 
as you make changes, write them on the listing until 


you run out of space (or you get tired). Then print a 
new listing. With an up to date listing, the worst 
that can happen is that you have to reenter all your 
code by hand. That fate is bad enough, but the 
alternative —having to write your code over again 
from scratch—is far worse. 

Backups on Disk. If you have the time, 
energy, and the money to afford extra disks, keep- 
ing backup copies of your source code is an alterna- 
tive solution. Simply copy the entire disk using the 
filer. Then put it in a disk box and put it away ina 
cool, dark place. If you lose your primary copy, or 
the cat tears it to shreds, your backup copy will save 
the day. 

Accidental Loss of Files. Occasionally, you 
might use the write command in the editor and type 
the wrong file name. If it is a new file name, there is 
no problem—simply use the filer to change it to 
what you originally intended. On the other hand, if 
it is the name of an already existing file, you have 
trouble. The file you just overwrote is destroyed by 
the copy of the new file you are editing. There may 
or may not be a way to recover a copy of the file that 
was overwritten. 

It may be that the reason you typed that par- 
ticular wrong name was that you just finished edit- 
ing that file. If you edit many different files in a row, 
you may get names confused. This is especially true 
if the names are similar: source1.text, source2.- 
text, source3.text, and so on. For the sake of argu- 
ment, assume that you are editing and saved a file 
named source2.text. Then you edited source3. 
text, but instead of saving it as source3.text, you 
write it to the disk using source2.text as the file 
name. What actually happens is this: the editor 
finds a block of space on the disk big enough to hold 
the new file; the editor writes out the new file; and 
finally the editor deletes the previous copy of the 
file. This last action creates a new block of 
<UNUSED> space on the disk, which actually 
contains the file you just “overwrote.” 

How do you recover from this mistake? If you 
realize the mistake right away, you have a good 
chance at recovery. Enter the filer immediately. 
Then use the M(ake command to create files that 
will occupy the blocks of space currently marked as 


< UNUSED >. For example, in the display shown 
earlier, you could issue the following sequence of 
commands: 


MAKE WHAT FILE? TMP1.TEXT[8] 

MAKE WHAT FILE? TMP2.TEXT[20] 
MAKE WHAT FILE? TMP3.TEXT[30] 
MAKE WHAT FILE? TMP4.TEXT[60] 


This will create the four files shown, each filling in 
one of the “holes” in the disk. Now edit the four files 
in turn, and see if any of them happens to be the file 
you just lost. If so, simply delete the remaining 
files, delete the new copy of the file you lost (which 
contains the wrong information), and finally rename 
the tmp file to the name of the file you lost. All this 
is complicated, but desperate situations call for 
drastic measures. 

Once you have created a file with the editor, it 
already has a file name that is known to the system. 
When you edit it again, use the S(ave command to 
write it back out to disk, instead of the W(rite 
command. This will avoid the pitfall I have just 
mentioned in 99 cases out of 100. 


The #*%%!!? ESCAPE Key 


In the editor there is one particularly nasty 
pitfall that you should know about. By realizing 
what can happen, you can work to avoid disaster. 
When you go into insert mode in the UCSD editor, 
it is possible at any time to cancel the entire text 
inserted by simple pressing the ESCAPE key. This 
action is absolute: there is no escape from this 
escape! Once you press the ESCAPE key, all that 
beautiful text you have just typed is wiped off the 
screen, thrown in the proverbial bit bucket, and 
gone forever. The system does not give you a sec- 
ond chance to decide if you really want to cancel 
your insert or not; it just does what you “tell” it. 

Notice that on the Apple II family of computers 
the ESCAPE key is located on the top left corner of 
the keyboard, right next to the key with the 
characters ! and 1 on it. (This is on Apple II and 
Apple [Je keyboards.) It is disgustingly easy to 
reach for the 1—! key and accidentally hit the ES- 
CAPE key instead. The moral of this story is this: 


143 


don’t stay in insert mode for very longbefore typing do, if it has been two hours since you started your 
“C to accept the insert. Sooner or later you are _ insert, the only alternative left to you is to have a 
going to hit the ESCAPE key by mistake. When you good cry. 


“C is Control-C 


144 


Preview of MAKEDESC and BROWSE 


Adventure 2 differs radically from Adventure 1 in 
its treatment of descriptions. All textual matter was 
embedded directly in the code of Adventure 1. In 
Adventure 2, most of the descriptions have been 
moved out of the program and into a disk file. The 
disk file containing the text of the descriptions, 
along with another file used as an index for the first 
file are collectively referred to as the descriptions 
database for Adventure 2. The next six chapters 
present the program used to generate this kind of 
database and describe the creation and use of simi- 
lar databases in adventure games. 


DATABASE CONCEPTS 


The term database is a very general one. It is 
used in many different contexts, not always in con- 
nection with computers. A database can be defined 
as any collection of information. More often than 
not the term implies that the information itself is 
organized for convenient retrieval. Subsidiary in- 
formation that is not part of the collection of infor- 
mation but is used solely to facilitate retrieval is 


often referred to as the database index. 

The databases for Adventure 2 and beyond are 
textual databases. The text consists of descrip- 
tions. In Adventure 2 they are descriptions of ad- 
venture game locations. In future adventures they 
may be descriptions of other things as well, includ- 
ing descriptions of events and speeches of charac- 
ters in the game. 


PREVIEWS OF CHAPTERS 19 THROUGH 24 


Chapter 19 contains the listings of two Pascal 
programs that I call MAKEDESC and BROWSE. 
The first of these is the program used to generate 
the descriptions databases. The second is used to 
examine descriptions databases outside adventure 
games. It may be used to preview your descriptions 
databases before you incorporate them into your 
own games. It also serves as an introduction to the 
techniques of retrieving data from databases. 

Chapter 20 is called “Creating a Database of 
Descriptions” and tells how to use the MAKE- 
DESC program. MAKEDESC processes a text file 


145 


that contains a mixture of two kinds of information: 


@ The actual text lines of the descriptions. 
@ Instructions to the MAKEDESC program itself. 


Chapter 20 tells you more about the nature of the 
instructions to MAKEDESC. It also describes the 
process of running the MAKEDESC program on a 
UCSD Pascal system and the information to provide 
to the program in response to various prompts. 
The title of Chapter 21 is “Random Access 
Files in UCSD Pascal.” It deals with the specific 
nature of file access. Both random and sequential 
files are defined and discussed in some detail. The 
extensions to Pascal needed to support random 
access files are described in the context of the 
UCSD system. The use of the BROWSE program 
for previewing descriptions databases is described. 
Finally, a discussion of how to incorporate descrip- 
tions databases into your own adventure games is 
given. In particular, the show procedure is de- 
scribed. The show procedure is used both by 
BROWSE and Adventure 2 to retrieve descriptions 
from the database and show them to the user. 
Chapter 22 describes the structure of the de- 
scriptions databases in detail. It is entitled “The 
Structure of Adventure Databases” and begins with 
a discussion of index files in general and the de- 
scriptions database index file in particular. The 
contents of the index entries are described in detail 
and revealed to consist of a collection of records. 
Each record in turn consists of a number of entries: 
name, id, dbegin, dend, and link. The function of 


146 


these entries, called fields, is described. 

The use of the database in an adventure game 
requires that the game declare and manipulate the 
database as a collection of files. The second part of 
Chapter 22 describes this process in detail. Finally, 
a discussion of the show procedure in the context of 
adventure games wraps up the chapter. 

Chapter 23 delves into the programming 
techniques used in MAKEDESC. It discusses con- 
cepts from the realm of computer language trans- 
lators including symbol tables, hash functions and 
hashing techniques, and lookup in symbol tables 
(linear search and hash lookup). Each of these 
techniques is discussed in general and in the con- 
text of adventure games and MAKEDESC. The 
advantages of hashing and hash table lookup are 
explained in comparison with linear sarch tech- 
niques. 

The manipulation of symbol tables is explained 
with the MAKEDESC code as an example. The 
operations of adding entries to the table and looking 
up entries in the table are both delved into. Chapter 
23 merely scratches the surface. If you find yourself 
fascinated by its concepts and techniques, a book on 
data structures, computer language translators, or 
compiler construction might be of interest to you. 

The section on MAKEDESC concludes with 
Chapter 24 which is entitled, “What Else Can You 
Put on Disk?” It is a short and speculative chapter. 
Several suggestions for extending the usefulness of 
adventure game databases are given. Some of the 
suggestions will be followed up in Adventure 3. 
Others are offered for your enjoyment and imagina- 
tion. 


MAKEDESC and BROWSE 


LISTING 19-1. MAKE80 DATABASE GENERATOR 


; i MakeDesc (aka Make80) and Browse80 
£ m a kr e d e 3 c > use a description file for input, and output 
f + a database file and an index file for use 
{ This program reads a file of text 3 byadventure games. See Chapter 20, 
{ cantaining a mixture of descrip- } Page 166 and in particular page 171 

{ tions and commands. It creates 3 for usage instructions. 

{ @utput files which may be used as } 

{ a database of textual passages. 3 Adventure 1 doesn’t use a database. 
{ An index file cantains information } 

{ about each passage: start and stop } Adventure 2’sdescription file 

{ indices in the data file (which is 3} _ isin AppendixB, page 286,and 

{ a file of strings of fixed length 3} should benamed“A2.DB80". 

{ 80), an identifier which may be } 

{ used if desired to retrieve the 3 Adventure 3's description file 

{ passage by name ~ say using a hash } is in Appendix C, page 292, and 

{ scheme, etc. A data file is also 3}  shouldbenamed“MTADV". 

{ created containing all the text * 

{ lines from the input, either padded} (Foryourownadventures, the file 

{ with blanks if the original line  }  canhaveanynameyoulike.) 

{ was less than 80 characters long, 3 


147 


ue 


or truncated ta 80 characters if 
original line was toa lang. 


he ke 


i 
i 
i 
i 
i 
| 
i 
i 
j 
j 
A 
} 
1 
| 
i 
i 
H 
! 
3 
Le 


ts Ie Ye ir Se 


FROGRAM makedescriptionss 
CONST 


hashmax = SOs 
blank4o an 


ory 


TYFE 


(indexsectiaon, descsection)s; 
STRINGCL4OT; 


whichsect 


i il 


pname 
storyline = STRINGC8OI; 

byte = 0. .2558 

procs = (pnamelookup, pentername, 


pdescribe, pmakename, 
pfindrec)s; 
placerec = 


RECORD 
CASE section:whichsection OF 


indexsectioan: ( tableentry: INTEGER ); 
descsections: ( name: pname; 
ids INTEGER s 
dbegins INTEGER; 
dend: INTEGER: 
link: byte 5 


END; 
VAR 
infile: texts 
narrates FILE OF STRINGCSOT: 
indexfiles: FILE OF placerec; 


148 


hash: ARRAYLO.,.hashmax] OF byte; 


symhash: INTEGERS 
where: INTEGER: 
{ index TO symbol table } 


places: ARRAYCO,.255] OF placerecs 


dumprecs placerecs 

lastrec: placerecy; 

nextplace: bytes { ainit=21 } 
outrecs: INTEGER: 


{ index TO descriptians. init=0 } 


placename: pname; 

lines storylines 

blank®oO: starylines 

iname, 

NNAME « 

xXNaAamMes STRING: 

is INTEGER: 

ditto: BOOLEAN; 

{ was last data line a ditto line? 3 

tracings BOOLEAN; 

che CHARS 

warnsets SET OF CHARS: 

{C init = C'S, 7" ,*e* 7] 
rome sem ene mn ste stm ane tt ess te ne etn nnn ts an is stn ns ae en we so tee a nes mt te "Ye 
{ t r a c e 3 
{ This procedure allows a trace of 3 
{ all procedure calls made by the 3 
{ program. This is most useful in 3 
{ the program debuqging phase. > 


PROCEDURE trace (whorprocs); 
BEGIN 


CASE who OF 


149 


pnamelookup:s writeln ( namelookup") 3; 
penternames: writeln (*entername’*)s 
pdescribes writeln (*describe’ >); 
pmakenames writeln (?makename’)s 
pfindrec: weiteln (*findrec’)s 


END ¢ CASE who 3; 


END { FROCEDURE trace 343 


. 
it 


Called by procedures on entry 
checks toa see if tracing is in 
effect and if so, calls the trace 
procedure toa print the name of the 
procedure ar function which is 
being entered. 


et be Le RS 


bs he bs he he 


i 
| 
1 
i 
i 
i 
j 
i 
! 
j 


exon ce en ss en es eA es of 


FROCEDURE inta (wha: proacs)s 
BEGIN 


IF tracing 
THEN 
REGIN 


write (’entering *); 
trace (who) s 


END; 


END ¢ FROCEDURE into 33 


Called by entername. Its job is 
ta determine if a given name has 
already been entered into the name 
table. The technique used is to 
traverse a linked list of name 
entries, all of which have the 
same hashvalue. The hashvalue is 


mre ra eee ene A et 
St ht bs bw he bs 8 he hs 


150 


f 
5 
£ 
‘ 
= 
co 


FUNCTION namelookup ¢ 
RE 


END ¢ 


ae i he hn hn Eh hn Th ln le i 
3 


computed by the caller 


7 


thisnames 
GIN 


into (pnameloakup) 
PlacesCOl].name := thisname; 
where := hash(Csymhashids 


WHILE placeslCwherel].name 

DQ 
wher @ 

{ endda 


= placestwhered].link 


namelookup := ( where “3 O )3 


FUNCTION namelookup 33 


This procedure is called in 
to enter a placename in the 


table. It is called whenever a 
placename instruction line (line 
beginning with a *$*) is scanned 


in the make8o source file. It 
calls namelookup to determine if 


the placename has been used before. 


PROCEDURE enternames 


VAR 

i: INTEGER; 
REGIN 

inta (pentername) ; 


symhash := OO; 
FOR i := 1 TO length 
DO 
symhash := 
(symhash + 


(placename) 


ord 


and left in 
the global variable “symhash’. 


order 
symbol 


i 
j 
H 


(placenameLlid)) 


! 
ho ket be 


pname ): 


thisname 


he BS Lt ke het he 


ee oe 


MOD 


BOOLEAN; 


(hashmax+1) 


151 


{ enddo 3 


writeln ("placenames *,placename); 
writeln (*hashvalue: *,symhash) ; 


IF namelookup (placename) 
THEN 
BEGIN 


writeln ("XX duplicate place:”*); 
writeln (placename) 3 


END 
ELSE 
BEGIN 


IF NOT ditto 
THEN 

placest€nextplacel.dend := autrec - 
< endif 33 


il 


IF nextplace =} © 
THEN 
lastrec 


= places(€nextplace] 
{ END IF 33 


nextplace := nextplace + 13 


WITH placestnextplacel] DO 


REGIN 
id = nextplaces 
link := hashE€symhashis; 
dbegin = outrec; 
name := placename; 
hash(€symhash] := nextplacey; 
ENDs 


END ¢{ IF namelookup 33 
ditto := false; 


END { PROCEDURE entername 35 


Adds a line of text to the des- 
the input line to be exactly 890 


this by padding the line with 
blanks. 


i 
i 
i 
H 


Cle te he he he ae i a 


PROCEDURE describe ( thisline: storyline )3; 


BEGIN 


into (pdescribe) ; 
IF length (thisline) «< 80 
THEN 


thisline := 
concat (thisline, 


capy (blank@o, 1, 80 - 


{ endif 33 

narrate” := thislines; 
seek (narrate, outrec); 
put (narrate) s 


outrec s= outrec + 1s 


END {€ FROCEDURE describe 3; 


PROCEDURE makename ( VAR thisname: 
BEGIN 


into (pmakename) ; 
IF length(thisname) «< 40 
THEN 


thisname := 
concat (thisname, 


criptions file. It first adjusts 


characters long. It accomplishes 


BY he hw net ke a es 


pname) 


capy (blank4oO, 1, 40-length 


length (thisline))} 


(thisname))) 


153 


writeln ("placename: *,thisname) ; 


END < FROCEDURE makename 33 


(re a faeces ty Senki sani sev ba-Sost) aban sith ees ao we Soon Sons cise leben va’ Geese nei voin ane iy sboud ado aoe, Se 3} 
{ f i n cd r e c 3 
{ } 
{ Handles the ditto instruction lines} 
{ from the make8O source file. It 2 
{ must distinguish between the case 3 
{ where a name is specified and the } 
{ case in which only a ditto was 3 
{ used. 3 
I me a meme ee en ees este et ee a tne we est tn tee neem ter nt tn ae nm enn tt ae nts anes nen ane" 


PROCEDURE findrecs 
BEGIN 


into (pfindrec) ; 

IF length (line) = 1 
THEN 

BEGIN 


astrec.dheqins 


placesCnextplaced.dhegin 1 
lastrec.dends 


places(nextplacel].dend = 
ce 


lastrec = places(€nextplaced 
END 
ELSE 
BEGIN 
line := copy (line, 2, length (line) - 1)3s 


makename (line) 
symhash := O; 
FOR a = 1 TO length (line) 
DO 
symhash := 
(symhash + ord (linelid)) MOD (hashmax+l) 
{ enddo }; 


IF NOT namelookup (line) 
THEN 
BEGIN 


wreiteln (error: ditto name does not exist’); 
writeln (line); 


154 


END 
ELSE 
BEGIN 


places(C€nextplacel].dbegin := 
placesCwherel].dbeqins; 


places(€nextplacel.dend 
placeslCwherel.dend; 


lastrec := placesEwhered; 
END { IF NOT namelookup 35 
END { IF length(line)?>1 33 
ditto := trues 


END {£ PROCEDURE findrec 33 


{-~ ce ‘sso a pmnapew ss Gennb aban shes, aes Spann eet mie om ni eet atts once soomn Si sai 20008 ‘sass tenet eons sna seete tenis ceean’ eoone seeee: ont 
{ 

{ om m aaaaaik k eeeee 88888 00000 

£ ommmm a a k ok e 8 8 © Q 

{ mmm a a kik e 8 8 QO 0 

Cc om m aaaaakk eee 8 Q QO 
{om m oa a kk e 8 8 Q Oo 

{ om m a a k ok e 8 8 Oo 8) 

{ om m a a k k eeeee 88888 O0000 

{ Here begins the main program block of makes8o 
{ The pracessing loop scans each line and 

{ decides whether to add it to the text part 

{ of the descriptians database, ar whether to 
{ interpret it as a makedesc instruction line. 
{ The instruction lines are handled by a case 
{ statement which uses the special character 

{ at the beginning of the line to determine 

{ which makedesc instruction to carry out. 

{ asi Sp bc ane Gi Sean pss Sasa ei ies Sst soi Sie ombs Suk cng ete Secs cds sleS day Sai em es so5e8 Shs con oa te i Se an a en sot Cebos HS ise at cess Se 
REGIN 


blank8O := concat (blank40, blank40); 
nextplace := 0; 
outrec 2= O88 


fet DP eS hd BP RS ee ee ke he as hw ke 


155 


FOR i s= © TO hashmax 


hashlid := 0 
{ enddo 34 


write (tracing? ===>") 5 
readin (ch)s 


THEN 

tracing #= true 
ELSE 

tracing := false 
{ endif 335 


2 


iname := "74 


WHILE length (iname) =O 
DO 
BEGIN 


write (“input file===>"); 
readin (iname);: 


END ¢£ DO 33; 

nname r= 7" 5 

WHILE length (nname) = 9 
DO 

REGIN 


write (‘description file===>")45 
readin (nname) ; 


END ¢ DO 33 
xname := concat (nname,*.x*) 3 


reset (infile, iname) 5 
rewrite (narrate, nname); 


Cale al ah 


a) 

4 

main processing loop } 
o a 

af 


156 


REFEAT 


readin (infile, line); 


IF length (line) = © 
THEN 
BEGIN 


ch s= linellids; 
CASE ch OF 
$s 
BEGIN 


line := copy (line, 2, length (line) — 1)35 
IF length (line) = 40 

THEN 

REGIN 


writeln ("placename too long’); 
write (*’chopping tos *)3 

line := copy (line, 1, 40); 
writeln (line) s; 


END { IF 33 
IF length (line) « 40 
THEN 

line := 

concat (line, 
capy (blank40, 1, 40 ~ length (line))) 

{ endif 33 
placename := line; 
entername; 


END: 


eae findrec: 


BEGIN 


157 


close (infile); 
iname := copy (line, 2, length (line)-1)3 
reset (infile, iname) ; 
END; 
END € CASE ch OF 33 


IF NOT (ch IN warnset) 
THEN 


describe (line) 


cn 


endif 33 


note: this takes advantage of the u.c.s.d. 
"fall through" semantics of the case 
statement and in general will not 
be transportable code. + 


a 


END ¢ IF length (line) ?0 3; 
UNTIL eof (infile); 


places€nextplacel.dend := outrec ~ 15 
{ finish last placerec } 


nextplace := nextplace + 1; 
PlacesCnextplacel.name := *nowhere’; 


Lr me ese mee tte tee te es ae tne tte mht ns ec ns tte 


€ write out index file 


r 


SL metre eee eee eens stte see tn ae anne ane ste ey nee sete arnt eee mn ett tam aes ens ee 


hw tw he 


close (narrate, lock)s 
rewrite (Cindexfile, xname); 


dumprec.section := indexsection; 
{ SET variant TO accept hash table entries } 


FOR i s= 0 TO hashmax DO 
BEGIN 


dumprec.tableentry := hashlids; 


158 


seek Cindexfile, i)43 
indexfile™ := dumprecs; 
put (Cindexfile); 

END £ DO 35s 


dumprec,. section := descsectians; 


{ SET variant TO accept place table entries 


FOR i 2= 1 TO nextplace DO 
REGIN 


dumprec := placeslid; 
seek (Cindexfile, i + hashmax)s 
indexfile™ := dumprec; 
put Cindexfile)s; 
END ¢ DO 35 
close (indexfile, lock); 
END. 
LISTING 19-2. BROWSE80 DATABASE SNOOPER 


{3} Comment brackets need an opening and closing bracket 


i 
: 
i 
$ 
i 
i 
} 
i 


b ~ Oo w 8S @ & © 


which have been generated by makego, 


eA eA eA ee ee en rl 
' : 


i 
i 
i 
i 
i 
i 
j 
H 
j 
i 
i 
! 
: 
i 
i 
i 


PROGRAM browse; 


CONST 
hashsize = BH; Use 30, not 31 
blank4o = 
: 40 spaces between apostrophes "4 
TYFE 
whichsect = (indexsection, descsectian) ; 


Fragram far previewing makedesc databases 


had 


RS hs he hs ke hw we 


159 


pname 
storyline 


byte 


procs 


placerec 


RECORD 


li 


STRINGC40]; 
STRINGCBOT; 


Oe. ees 
(pnamelookup, pentername, 


pdescribe, pmakename, 
pfindrec); 


CASE section:whichsectiaon OF 


indexsection: 


descsection: 


ENDs 


VAR 


infiles 
narrates 
xfiles 


hashs 
symhashs 


where: 


{ index TO symbal table 


places: 
dumprecs 
lastrecs 


starts: 
stop: 


nextplaces 


160 


( tableentry: INTEGER )3; 
( name: pname; 
id: INTEGER; 
dbegins INTEGER; 
dends INTEGER: 
link: byte 5 
texts 


FILE OF storyline; 
FILE OF placerecy} 


ARRAYCO,.hashsize] OF byte; 
INTEGER: 


INTEGER 


Hy 
ARRAYCO..255] OF placerecy 
placerecs; 


placerec; 


INTEGER; 
INTEGER; 


bytes £ init= 0 } 


outrec: 


r 


placename: 
line: 
blank@os: 


iname, 
NNamMe, 
xnNAamMes 


is 
ditto: 
{ was last data 


tracing: 


ches 
warnsets 
{ init = (C’*$*,?* 


{ index TO descriptians. init=0 } 


INTEGER; 


ba 


pnames 
storylines 
storylines 


STRING; 


INTEGER: 
BOOLEAN; 
line a ditto line? } 


BOOLEAN; 


CHAR; 
SET OF CHAR; 


we os ys , 
a o ] e 


FROCEDURE trace (whorprocs) ; 


BEGIN 
CASE who OF 


pnamelookups 
pentername: 
pdescribe: 
pmakename: 
pfindrecs 


END ¢ CASE who 


writeln(*?namelookup’ 
writeln ( entername’ ) 
writeln(* describe’); 
writeln (* makename’* ) 3: 
writeln(* findrec”®)s; 


+ 
a 


END { PROCEDURE trace }5 


PROCEDURE into (whos procs); 


BEGIN 


~ 


161 


IF tracing 
THEN 
REGIN 


write (‘entering *)5s 
trace (who)s 


END: 


END { PROCEDURE into 33 


FUNCTION namelookup ¢ thisname: pname ): BOOLEAN; 
REGIN 


into (pnamelookup) ; 
places(CO].name := thisname; 


where >= hash€symhashd; 
WHILE placesCwherel.name <> thisname 
DO 


where := places Cwheredld. link 
£{ enddo 3; 
namelookup == ( where <> 0 )3 


END ¢ FUNCTION namelookup 33 

{ qd @ S$ ¢ r i b @ 3 
3 

FROCEDURE describes 

BEGIN 


into (pdescribe); 


start := placesCwherel.dbegins 
stop := places(C€wherel.dend; 


FOR i s= start TO stop 


162 


seek (narrate, i): 
get (narrate) ; 
write (narrate) ; 
VD ¢ DO }; 


END { PROCEDURE describe 3; 


i 
} 
i 
! 
} 
{ 
t 
H 
| 
i 
t 
i 
i 
: 
i 
i 
kes ae 


! 
i 
i 
' 
H 
' 
i 
H 
i 
H 
Re 


FROCEDURE makename (VAR thisname: pname? 
BEGGIN 


into (pmakename) : 
IF length (thisname) «= 40 
THEN 


thisname := 
concat (thisname, 
copy (blank40, 1, 490 —- length (thisname))) 


{ endif 33 
writeln (*placename: *,thisname) ; 


END ¢<¢ PROCEDURE makename 3; 


BEGIN 


blank80O := concat (blank40, blank40O); 
nextplace := 13 
write (tracing? ==="); 
readin (ch); 
IF ch="y* 
THEN 
tracing := true 
ELSE 
tracing := false 
{ endif 3 


nname := "75 


WHILE length (nname) = 0 


163 


DO 
REGIN 


write ("description file===>"); 
readin (nname); 


END ¢ DO Fs: 
xname := concat (nname, 


reset (xfile, xname); 
reset (narrate, nname) ; 


FOR i s= © TO hashsize DO 
BEGIN 


hashlCild = xfile*.tableentry; 
get (xfile); 


END; 

ios= 13 

REPEAT 
placeslild := xfile“; 
get (xfile); 
ios= i + ds 

UNTIL eof (xfile)s 


close (xfile, lock); 


Ls ht 


Lf 
\. 
{ main processing loop 
{ 


REPEAT 


write (’describe what place===>"); 
readin (placename) ; 


IF placenamef1l] = °° 
THEN 
BEGIN 


164 


7 
cS 
Al 
~ 


= 0 TO hashsize 


where := hashlid; 
WHILE where <2? © 
DQ 

REGIN 


writeln (places{€wherel.name>) 4 
where := placesCwherel],.link 


END {€ WHILE where <> © DO 33 


END < FOR i := 1 TO hashsize DO } 


END 
ELSE 
BEGIN 


makename (placename) 5 


= O04 
FOR i := 1 TO length (placename) 


symhash := 


“ae 


(symhash + ord (placenamelid)> MOD 


{ enddo 3; 


IF NOT namelookup (placename) 
THEN 


writeln ("no such place’*) 


describe 


{ endif 3s 


UNTIL placename = *quit 


END. | 


36 spaces 


(hashsizetl) 


165 


Creating a Database of Descriptions 


You may have noticed in Adventure 1 how much of 
the program consisted of writeln statements. Put- 
ting all that creative prose straight into the program 
takes up an enormous amount of space. The space 
could be used for other Pascal code; code that could 
make the game more complex, devious, creative, 
and challenging. It would be better if most (if not 
all) of the text of adventure descriptions were 
stored outside the program code. The natural place 
for such storage would be on the disk. The adven- 
ture program could then refer to the disk whenever 
it needed to describe a particular location. The 
program space freed by removing descriptions can 
then be used to add more problems, locations, and 
challenges to the game itself. 

How can this idea be put into practice? There 
are some new problems to solve! 


1. How to create a file containing the text of ad- 
venture game descriptions. 

2. How to make sure the file is structured so that 
the description of any location can be quickly 
retrieved. 


166 


3. How to write Pascal procedures in adventure 
games(s) that read the descriptions from the file 
created in step 1. 


In this and the next few chapters, I shall at- 
tempt to help you solve these problems. In this 
chapter, I describe a utility program which solves 
problem 1. 

The utility program or “tool” is called 
MAKEDESC. A listing of MAKEDESC was in- 
cluded in Chapter 19. You can type it into your 
system and use it. MAKEDESC generates or 
creates a database of location descriptions for use in 
your adventure game. What, you may ask again, is a 
database? A database is any collection of informa- 
tion. Your kitchen recipe file is a database. The files 
in the office of any business form a database. Your 
income tax records form a database. However, 
when computer people use the word database to 
refer to a collection of information, they usually 
have another requirement in mind: 


The information in a database is structured for 
easy retrieval. 


MAKEDESC creates a database of descriptions MAKEDESC: THE DESCRIPTIONS GENERATOR 
of adventure game locations. It is made so that you MAKEDESC has the following inputs and out- 
can easily retrieve the descriptions using a simple puts: 
Pascal procedure. I will show you how to do that in 
Chapters 21-23. In the remainder of this chapter,1 >> Input: one or more text files telling 
discuss how to use MAKEDESC to create the de- MAKEDESC what the descriptions are and 
scriptions database in the first place. how to divide them up into different locations. 


Create textfiles containing 
descriptions plus MAKEDESC 
instruction lines (explained 

in the text). 


X(ecute the MAKEDESC (MAKE80) 
program, specifying your 

first textfile as input. 

MAKEDESC will prompt for this 
information. 


Descriptions 


1) Text 
MAKEDESC Database 


Descriptions b program (fn) 
Text file processing 
(or files) 


J 


Descriptions 
Index 

File 

(fn.x) 


Fig: 20-1. A schematic diagram of the usage of the MAKEDESC program. 


167 


>> Output: two files—an index and a descriptions 
file. 


The process of using MAKEDESC is illus- 
trated schematically in Fig. 20-1. The input file to 
MAKEDESC will consist of ordinary text, which 
forms descriptions of locations, interspersed with 
MAKEDESC instruction lines. Here is part of a 
typical MAKEDESC input file: 


$At the end of a dusty road 

You are standing at the end of a dusty road. 
In the distance | can see a brick building with 
a well in front of it. 

$In a maze of confusing tunnels 

You are in a maze of twisting, confusing 
tunnels. Everything looks alike in here. | am 
having great difficulty finding my way. 
$maze2 


$Near a volcano 

You are near an active volcano. | can hear 
rumbling and | smell a sulfurous odor in the 
air. Even though it is dark here, a faint red 
glow shatters the darkness and suffuses the 
air with light. 


Any lines beginning with one of the MAKE- 
DESC instruction characters shown in Fig. 20-2 
will be processed by MAKEDESC as special in- 
structions to itself and not as part of the descrip- 
tions. 

All other lines will be taken as textual descrip- 
tions of a particular location. I will refer to such 
lines as description lines. Description lines must be 
40 characters or less in length. This restriction is 
there because MAKEDESC was originally written 
on an Apple II without an 80 column display card. 
The text display of the Apple II is only 40 characters 
wide. If you want to modify MAKEDESC to allow 
longer description lines, you may. Later on, I'll 
show you how. 


HOW TO USE MAKEDESC INSTRUCTION LINES 
There are three types of instruction lines: 


168 


Fig. 20-2. The MAKEDESC instruction characters. 


1. Placename lines—instruction lines beginning 
with a $. 

2. Ditto lines—instruction lines beginning with a 

3. Append file lines—instruction lines beginning 
with a >. 


Placename Instruction Lines 


MAKEDESC requires that the description of 
each location, which may be many lines long, be 
immediately preceded by a placename instruction 
line. Each placename instruction line tells 
MAKEDESC two: things: 


1. Stop collecting the description of the last loca- 
tion. 

2. Create anew location with the name provided in 
the placename instruction line and prepare to 
start collecting its description. 


Suppose the input contained the instruction line: 
$Pit of Darkness 


At this point MAKEDESC would close off its cur- 
rent description. It would create a new location 
whose name would be “Pit of Darkness” and as- 
sociate subsequent description lines with that loca- 
tion. The description of the Pit of Darkness would 


encompass all description lines in the input up to 
(but not including, of course) the next placename 
instruction line: 


$Pit of Darkness 

You are in a Pit. It is pitch black. 

The darkness is so great you cannot see the 
brightly burning lantern you hold at arm’s 
length. The light from the lantern is quickly 
absorbed by the greedy, all encompassing 
blackness. You can almost feel the dark- 
ness, like a sinister, oily, choking, sub- 
stance. You are very close to panic. 

$A long narrow tunnel 


The description of the Pit of Darkness will be 
taken as all the text lines between the two 
placename instruction lines. 

$Pit of Darkness 
and 

$A long narrow tunnel 


There are some restrictions on the use of 
placename instruction lines: 


@ You may not have duplicate placenames in your 
input. In the previous example, if MAKEDESC 
encountered a second placename instruction line 


$Pit of Darkness 
it would complain, and tell you: 
*«* duplicate placename: Pit of Darkness 


The way MAKEDESC is currently written, it will 
also mess up the old description of Pit of Darkness 
in such a situation—so my advice is—Don’t do it! 


@ Placenames themselves, such as “Pit of Dark- 
ness” or “A long narrow tunnel” must be 40 
characters or less in length. If you put in a 
placename that is longer than that, for example 


$The Brobdignagian gold, silver, diamond, 


and platinum mines 


MAKEDESC will chop off everything after the for- 
tieth character and issue the message 


Placename too long. 
Chopping to: The Brobdignagian gold, 
silver, diamond, 


This will not do any harm to the subsequent 
descriptions of the place. However, the placename, 
if used in an adventure game program will be per- 
manently “stunted.” 

This restriction is more or less arbitrary. 
Later we will show you how to get longer place- 
names (they just take up more space). 


m@ There may be no more than 255 placenames in a 
given MAKEDESC database. This is a limitation 
imposed by the way that the internal MAKE- 
DESC data structures were designed. 


DITTO LINES 


Instruction lines that begin with quotation 
marks (“) are known as ditto instruction lines. They 
are provided mainly for convenience, and may be 
used to repeat a previous description. They are to 
MAKEDESC as ditto marks are to handwritten 
manuscripts. 

There are two ways to use “: 


@ Without a placename. 
gw With a placename. 


If you use “ all by themselves, MAKEDESC 
will repeat the description of the last placename 
(this includes the possibility that the last placename 
was itself described using a ditto instruction line). 
For example, suppose you want to put a maze of 
“twisty little passages, all alike,” as in the original 
adventure, into your own adventure database. 
Study the following: 


$maze1 

You are in a maze of little 
twisty passages. They all look 
alike. 


169 


$maze2 


“c 


$maze3 


$maze4 


“ 


$maze5 


Notice first the ditto lines. Each of them will 
cause the corresponding placename instruction to 
use the description given under the place maze1. 
Notice also that for each new place that uses a ditto 
instruction line, a separate placename instruction 
line is required. So it is useless to try saying 


$maze 

You are in a maze of little 
twisty passages. They all look 
alike. 


This doesn’t work because MAKEDESC is too 
dumb to create a place without aname. When it sees 
a ditto instruction line, it assumes that you have 
already created a new place that has been described 
by a placename instruction line. 

If anything follows the “ on a ditto instruction 
line, MAKEDESC does something different. It will 
try to interpret whatever follows the “ as the 
placename of an already existing place. If it suc- 
ceeds in doing so, it will use the description of that 
place as the description of the place to which the 
ditto applies. You really need an example to under- 
stand this one! 


$maze1 

You are in a maze of twisty little 
tunnels, all alike. 

$maze2 

You are at a dead end in the maze. 
$maze3 

“ maze 

$maze4 


170 


“ maze2 
$maze5 


“ maze 


In this example, you have created 5 places: 
maze1, maze2, maze3, maze4 and maze5. 
There are two descriptions: the first applies to 
maze1, maze3, and maze5; the second applies to 
maze2 and maze4. 

Caution. The name (if any) used in a ditto 
instruction line must be the name of an already 
described place. The name following “ is not used to 
create a new place. You must create the place first 
by including a placename instruction line. Study the 
above carefully to help clarify these points in your 
mind. 


CONTINUATION FILE LINES 


Textual material takes up lots of space, even 
on disk. On many systems, if you are writing a 
really big adventure game, you won't be able to fit 
all the input to MAKEDESC into a single UCSD 
Pascal text file. Because I don’t want to limit your 
ability to create lots of exciting descriptions, I have 
given you a way to “chain” text files together when 
you are using MAKEDESC. Lines beginning with 
the > character are called continuation file instruc- 
tion lines. 

If the last line of a MAKEDESC input file is 


>advent2.text 


MAKEDESC will stop processing that file and con- 
tinue with the file named advent2.text. Any lines 
that follow a continuation file instruction line will be 
ignored by MAKEDESC. That is, when it goes toa 
continuation file for further input, it never returns 
to the original file. 

On the other hand, there is no limit to the 
number of continuation files that MAKEDESC can 
process in a given run. Well, that’s not quite 
true—there is always the limit imposed by the total 
amount of disk space available! Think of continua- 
tion files as being “in line” to be processed by 
MAKEDESC. This is illustrated schematically in 
Fig. 20-3. 


First input file 


> fileb.text 


Continuation 
input file 


fileb.text 


Fig. 20-3. The effect of the use of a continuation instruction line. 


RUNNING MAKEDESC 

Now, how do you actually use the MAKE- 
DESC program on your own computer? First, of 
course, you must obtain an executable version of 
MAKEDESC. To do this, perform the following 
steps: 


1. Enter the source code of MAKEDESC as given 
in Chapter 19 into a .text file, using the UCSD 
Pascal editor. 

2. Compile MAKEDESC.TEXT to produce 
MAKEDESC.CODE. 


Details on these steps will be found in the 
documentation for the UCSD Pascal system on your 
particular computer. 

Once you have compiled MAKEDESC. TEXT 
to obtain MAKEDESC.CODE, you need to con- 
struct input for MAKEDESC to “chew” on. This 
will be constructed, again using the UCSD Pascal 
editor. It will consist of a mixture of place descrip- 


tions and MAKEDESC instruction lines, as de- 
scribed in the first part of this chapter. You will 
create one or more .text files containing this input. 
If you have more than one file, make sure to include 
the appropriate continuation file instruction lines 
needed for MAKEDESC to link them together. 

To execute MAKEDESC, simply type X in 
answer to the UCSD system level prompt. Then 
when the system asks: 


Execute what file? 


respond with 
MAKEDESC 


One word of caution is in order here. If you 
have more than one disk drive attached to your 
system, you may have to answer the prompt dif- 
ferently. For example, if you have two drives, and 
MAKEDESC is on the “nonboot” drive, you may 
have to type: 


171 


See “A note about disk Drive 
numbers in file names and 


commands” on page 3 Of creates. There is one slight restriction here: the 


#5:MAKEDESC 
this PDF. 

What you type will depend on whether or not you 
have entered the Filer program and issued a prefix 
command. For details on the concepts of volumes, 
file names, prefixes, and so on, consult your “local” 
UCSD manual. 

After you succeed in answering the prompt 
correctly, MAKEDESC will be loaded and it in turn 


will prompt you as follows: 
Program looks only for 


lowercase “y”, all other 
input is treated as “no” 


This prompt asks whether or not you would like to 
see a dynamic trace of all the procedure names in 
the program as they are invoked. It was built into 
MAKEDESC as a debugging aid. Unless you are 
curious and insist on seeing this information, you 
may safely “answer” this prompt by pressing the 
RETURN key. 
The next prompt will be: 


TRACING?=== 


INPUT FILE===> 


Here you should respond with the file name of your 


first input .text file; for example 
* 


INPUT FILE===> #[5:ADVENT1.TEXT 


Notice that a volume number may be necessary, as 
before. 
Next you will see 


DESCRIPTION FILE===> ** 


This is the last prompt from MAKEDESC and the 
name you type in response will be used by 
MAKEDESC as the name of the output file that it 


* The square bracket above is probably a typo. 


description file name may be at most 13 characters 
in length. The reason for this is as follows. 
MAKEDESC actually creates two output files: the 
descriptions data file and the descriptions index 
file. 

The descriptions index file is used by your 
adventure game to quickly locate the part of the 
descriptions data file containing a given descrip- 
tion. How all this works will be explained in sub- 
sequent chapters. For now, however, all you need 
to know is that the descriptions index file name is 
obtained from the descriptions data file name by 
appending the string .x (the X is for indeX). All file 
names in the UCSD system must be at most 15 
characters in length. Therefore, the descriptions 
data file name, which by definition is two characters 
shorter in length than the descriptions index file 
name, must be at most 13 characters in length. 

After you answer the prompts, MAKEDESC 
will chug away building your descriptions database. 
As each new placename instruction line is encoun- 
tered by MAKEDESC, it will echo the placename 
to the terminal screen: 


placename: Above ground 
placename: In a pit 
placename: Up a tree 


and so on. This will give you feedback on the pro- 
gress of MAKEDESC. It will also let you know 
where MAKEDESC was should anything go seri- 
ously wrong during processing. 

When MAKEDESC finishes processing, it will 
have built two files, as described earlier. These 
files may then be used in your own adventure game 
programs. How this is done will be described later. 


** Just type the database name with no extension. For example “advent1” instead of “advent1.text” 
(in this example MakeDesc will output two files: “advent1” and “advent1.x”) 


172 


Random Access Files in UCSD Pascal 


The program MAKEDESC described in Chapter 20 
produces a database of adventure game descrip- 
tions. This database is stored in two UCSD Pascal 
files: an index file and a data file. The index file is a 
sequential file, but the data file is a random file. 
This chapter will explain the concept of random file 
and discuss its implementation in the UCSD Pascal 
language. 


WHAT ARE RANDOM AND SEQUENTAL FILES? 


All files contain records. A file all by itself is 
not sequential or random, although people some- 
times use the term random file. It is the access to the 
records in a file that may be either sequential or 
random. So when I say “sequential file,” I mean 
“file accessed sequentially.” When I say “random 
file,” I mean “file accessed randomly.” 


Sequential Access 

When you are using sequential access to a file, 
the records must be retrieved in the order in which 
they are stored. Access usually begins with the first 


record in the file. Therefore, in order to access the 
last record in a file, using sequential access, you 
must first access all the records in the file that come 
before it. A file that is accessed sequentially is like 
aroll of candy or breath mints as shown in Fig. 21-1: 
in order to get to the last candy, you must first 
remove all the ones before it. 

This is fine and dandy—when it comes to 
candy! Database files are a different story. Con- 
sider an adventure game descriptions database. 
Suppose you accessed the place descriptions se- 
quentially. To reach the description of the last 
place, you would have to read through (but not 
display) all the place descriptions preceding the last 
one. In general, you would have to read through any 
descriptions preceding the one you wished to show 
to the player. You would be sitting around waiting 
most of the time! You say you don’t believe that — 
let’s do some calculations. 

Suppose your adventure game has 100 loca- 
tions and the descriptions of these locations aver- 
age five lines apiece. The file of these descriptions 
would contain approximately 500 lines, each being 


173 


Fig. 21-1. An example of sequential access in real life. 


readable as a Pascal string variable. Then, on the 
average, you would have to read 250 strings in 
order to reach the specific one that begins a par- 
ticular description. Now I hear you saying to your- 
self: “Wait! Computers are fast. They can read 
those strings in no time at all.” But consider that a 
typical minifloppy disk access takes 50 milli- 
seconds—that is 50/1000 seconds or one-twentieth 
ofasecond. Then in order to read 250 strings, it will 
take 250 x 1/20=12.5 seconds. This means that on 
the average, you will wait twelve seconds before 
you see the description of the next place. Most 
players will probably want responses to be dis- 
played more quickly than this. You can try this 
experiment on your own system to see how far off 
my estimates are. Here are two short programs to 
use. The first creates a file of 500 strings. The 
second reads through the file sequentially until it 
gets to the 250th string in the file. Try them out and 
use your stopwatch! 


THE FILE CREATING PROGRAM 


FROGRAM wirtxts 
VAR 


recs STRING(SO)]; 
fz FILE OF STRINGLS8OI; 
is: INTEGER; 


BEGIN 


rec := Press Return after the equal sign 
"You are in mountains. Tea 
see all the snow-capped 
monsters hereabouts,’s 


rewrite (f, “junk.text*) 3 
FOR 2 s= 1 TO SOO DO 
REGIN 

fC" f= recs 

put (fds 

END; 

close (f, lock); 

END. 


THE FILE READING PROGRAM 


FROGRAM rdtxts 


VAR 
rec: STRINGCS8OI; 
f: FILE OF STRING(S&O7; 


i: INTEGER; 


BEGIN 
reset (f, “junk.text*): 
writeln (* Commencing 


reading.«.")% 


FOR i = 1 TO 250 DO 
BEGIN 

f° f= recy 

get (f)5 

END; 

writeln ("Got it"): 
writeln (f%)5 

close (f, lock); 
END. 


Type as 
one line 
followed 
by Return 


In both of these programs as written, ‘junk.text’ is written to and read from Drive 1 (Volume #4:) 


174 


See “A note about disk Drive numbers in file names and commands” on page 3 of this PDF. 


RANDOM ACCESS 


Random access to a file means that any par- 
ticular record in the file may be retrieved directly. 
No other records must be read first. A randomly 
accessed file is like the same roll of candy—but 
with the wrapper removed! You can grab any of the 
candies you like at any time. 

There is a price to be paid for the ability to 
access a file randomly: 


All records in a random access file must be of 
the same fixed size in order for random access 
to be practical. 


In order to understand this limitation, you 
must delve into the workings of random access 
files. Here is roughly how they “work.” 

Each record in the file is a certain “distance” 
from the beginning of the file. This distance is easy 
to calculate from two pieces of information: 


1. The size of the records in the file (which I have 
assumed is the same for all records in the file). 

2. The number of the record sought. Records are 
assigned numbers, beginning with the number 
0, according to their position in the file. 


Once the position of a given record is calcu- 
lated, the computer hardware can be given instruc- 
tions to seek to that position in the file. This means 
that the disk drive’s read mechanism is moved in 
such a way that the record in question will pass 
under the read head. Then, simply reading a record 
from the file will produce the desired result. It is 
almost as if each record were its own tiny, separate 
file. Of course, all this depends on the ability to 
calculate the position of the record. 


Limitations of Random Access 


The concept of a random access file is not part 
of Standard Pascal. It was added to the UCSD Pas- 
cal language and must be considered an extension. 
Actually, that is not quite true—you may declare 
files of fixed size records in Standard Pascal. In fact, 
because every file consists of records of some pre- 
declared Pascal type, they are automatically of 


fixed size. (The exception to this is the text file: 
FILE OF CHAR. A “record” may be thought of as a 
line of text that is terminated by an anonymous 
internal EOL.) 


UCSD Pascal and Random Access 


The UCSD Pascal language has added support 
for accessing files randomly. This support is sur- 
prisingly simple. It consists of a single new intrinsic 
procedure called seek: 


seek (fileid, recnumber); 


This procedure can be called to p»sition the file to 
the correct record, according to the value contained 
in recnumber. Following a call to the seek proce- 
dure, a call to get or put may be made, just as if the 
file were being read or written sequentially. 

There are some rules to remember when using 
seek: 


@ Don’t do two seeks in a row without an interven- 
ing get or put. 

@ Remember that the get or put that follows a seek 
moves the file window. This is just the same as if 
no seek had first been performed. 

gw EOF (f) is always false after a seek has been 
performed. 


You may ignore all this if you don’t plan to use 
seek in your own programs. The use of seek in the 
adventure games in this book is always the same. 
The code is already written and works—you may 
simply imitate it and you won’t go wrong. 


THE BROWSE PROGRAM 


In Chapter 19, I presented the listings of two 
Pascal programs. One is the MAKEDESC adven- 
ture database generator program. The use of 
MAKEDESC was described in Chapter 20. The 
other is BROWSE, a program that may be used to 
review the output of MAKEDESC before including 
that output in one of your adventures. 

BROWSE uses all the same declarations as 
MAKEDESC. It starts out by prompting you for the 
file name of your descriptions. This is read by 


175 


BROWSE and used to open the descriptions file for 
reading. The name of the description file is used to 
derive the name of the index file, as described in 
Chapter 20. The index file is also opened for read- 
ing. BROWSE uses the index file and the descrip- 
tions file in the same way that adventure games do. 

The BROWSE program prompts the user for a 
placename. You may respond with one of the 
placenames used in generating the descriptions 
database. In this case, BROWSE will look up the 
place by name and print its description on your 
terminal. If you don’t have a list of the placenames 
handy, you may also type ? in response to the 
prompt for a placename. In this case, BROWSE will 
print a list of all the placenames in the database you 
are reviewing. 


USING DESCRIPTIONS 
DATABASES IN YOUR ADVENTURES 


If you read Adventure 2 carefully, you will see 


176 


how to incorporate the databases generated by 
MAKEDESC into your own adventures. There are 
several key points to remember: 


w Declare the relevant types and variables. These 
are discussed in more detail in the next chapter. 


m@ Make sure to open the descriptions data and 
index files in the initialization procedure in your 
adventure. 


m@ Incorporate the show procedure, used to re- 
trieve and display each description from the 
database. The show procedure is almost identi- 
cal to describe procedure in the BROWSE pro- 
gram. More detail on how describe and show 
operate will be given in the next chapters, which 
discuss the details of the structure and pro- 
gramming techniques associated with the de- 
scriptions database generated by MAKEDESC. 


The Structure of Adventure Databases 


In Chapter 21 I briefly described the files compris- 
ing an adventure database. In this chapter I shall 
delve into the detailed makeup of those files. In the 
process, I shall elaborate on several topics: 


@ Index files in general and the descriptions index 
in particular. 

@ The use of Pascal variant records in building the 
index file. 

@ The incorporation of the database in Adventure 
2—the use of the show procedure for retrieving 
the description of a place and so on. 

m@The BROWSE program for previewing your 
database after creating them. 


INDEX FILES AND THE DESCRIPTIONS INDEX 


An index file is analogous to an index in a book. 
It contains information about where in the data 
file(s) of a database specific entries may be found. In 
the adventure database, the data consists of de- 
scriptions of places or locations, in the adventure. 
The index file consists of entries, each of which has 
information about a specific location. 


The adventure database index file actually has 
two sections, as shown in Fig. 22-1. The first sec- 
tion is an array of numbers known as hash values. 
The techniques of hashing and hash table lookup are 
discussed in the next chapter. They are mainly of 
interest to the MAKEDESC program, which 
creates the database, and the BROWSE program, 
which may be used to preview the database. Ad- 
venture 2 does not use the hash table part of the 
index itself. The use of the hash table is a key to 
being able to retrieve the description of a location 
given its name. This information is quite vital to the 
operation of MAKEDESC in particular, because 
MAKEDESC checks for the presence of duplicate 
placenames. 


The second part of the index file is a collection 
of records that I shall call description information 
entries. Each of the records contains the information 
needed to locate the description of one particular 
adventure location. The Pascal type declaration 
that isin MAKEDESC, BROWSE, and Adventure 2 
reveals the nature of the information contained in 
these records. 


177 


section 


placerec = 
RECORD 
CASE section: whichsection OF 

hashsection: (tableentry: INTEGER); 

descsection: ( name: pname; 
id: INTEGER; 
dbegin: INTEGER; 
dend: INTEGER; 
link: byte ); 

END; 


This declaration is an example of what is known in 
Pascal as a variant record type. It enables the infor- 
mation stored in variables of that type to be 
nonhomogeneous. The case statement in the rec- 
ord declaration introduces the variants. Each case 
label in the declaration represents one of the possi- 
ble variations that the record can take on. In this 
example, the record type placerec has exactly two 
variants—hash section and descsection. The first 
variant accounts for the records in the hash-table 


178 


Fig. 22-1. The format of the index file of a 
descriptions database. 


section of the index file. The second variant ac- 
counts for the remainder of the file, which consists 
of one record for each description in the data portion 
of the database. 

All three programs, MAKEDESC, BROWSE, 
and Adventure 2, use the placerec data type in the 
declaration of a file: 


VAR 
xfile: FILE OF placerec; 

The file window xfile ~ always contains a record 
from xfile. The structure of that record may match 
the hash section record or the descsection record. 
This depends on which variant of the record type 
has been selected by the program. The section 
variable declared in the case statement of the rec- 
ord type declaration is used to control and re- 
member this at run time. I will touch on this again in 
Chapter 23 when I discuss the operation of the 
BROWSE program. 


Contents of the Descriptions Index Entries 


The descsection record structure shows sev- 
eral fields in the corresponding records. Each of 
these fields is present in a descriptions index entry 
for a location. The fields are described as follows: 


@ name 

This is a string containing the name of the loca- 
tion as it was present in the “source” file that 
MAKEDESC used to process this entry when the 
database was generated. The user-defined type 
pname is used to limit the size of the string in 
order to conserve file space. The value 20 was 
chosen as the maximum length for values of this 
string. The names of locations could be used as 
shorter descriptions, in which case it might be 
desirable to allow for a slightly longer string. 


wid 

This is an integer that records the positional 
value of the individual location in the list of loca- 
tions. It is a redundant piece of information that 
was used in debugging MAKEDESC originally 
and was never removed from the declaration of 
placerec. It “wastes” two bytes of disk space per 
index entry. If you feel that this is an intolerable 
misuse of disk space, you can easily remedy the 
situation. Simply remove the id: field from all 
declarations of the placerec data type. 


@ dbegin 

This is an integer that gives a record number in 
the data file of descriptions. It is the first record 
number for the description of the location to 
which a given index entry corresponds. It tells 
the show procedure or other retrieval code how 
to get to the right place on the disk in order to 
start reading the location’s description. 


@ dend 
This is an integer that gives another record 
number in the data file of descriptions. It is the 
record number of the last line in the description 
of the corresponding location. It tells the show 
procedure when to stop reading description lines 
from the data file. 


*See “A note about disk Drive numbers in file names and commands” on page 3 of this PDF. 


@ link 
This is a value between 0 and 255 that is used in 
the hash table lookup algorithm. It determines 
another index entry to examine should this one 
not correspond to the specific name being 
sought. The details of how this works will be 
revealed in the next chapter. 


The structure of the descriptions database 
files and their relationships are in some detail 
shown in Fig. 22-2. 


USING THE DESCRIPTIONS 
DATABASES IN ADVENTURE GAMES 

Once a descriptions database for a given ad- 
venture has been generated using MAKEDESC, 
there still remains the question of how to access 
that database in an adventure game. Adventure 2 
presents a model for accomplishing this. 


Initialization of the Database 


There are two files that need to be made ac- 
cessible to the adventure game: 


FILE OF placerec; 
FILE OF storyline; 


xfile: 
narrate: 


The narrate file is the descriptions data file. It only 
needs to be opened in order to be accessible for 
reading later on. This is accomplished in the ini- 
tialization procedure in Adventure 2 via the state- 
ment: 


reset (narrate, “a2.data’); * 
The xfile file is the index file and has been discussed 
in detail above. It must be read into memory by the 
adventure game in order to be used for retrieval 
purposes. The xfile file is opened by the statement: 
reset (xfile, “a2.data.x’’); * 
Then the following code reads in the index file and 


stores the descsection records in the array called 
places: 


179 


Indexfile: A2.DB.X 


Datafile: A2.DB 


All the text 


for the 


description of 


this location 


FOR i := dbegin TO dend DO 
BEGIN 
seek (narrate, i) 


get 


(narrate); 


write (narrate™)s; 


ENDs 


The seek command in this code accounts for the 
fact that none of the has‘section records stored in 
the index file are actually used by the adventure 
game program. The records numbered 0 to 31 in the 
index file are all hash section records. Therefore, in 
* hash, not has 

180 


Fig. 22-2. The file structure of the entire 
descriptions database. 


The two sections of code 
shown on pages 180 and 181 
are swapped. 


order to skip over them, the initialization code first 
“seeks” to position 32 in the index file. After that, 
each call to the I/O get procedure will advance the 
record pointer one position in the index file. That is 
why no further calls to seek are necessary inside 
the repeat loop. 


The Operation of the show Procedure 


Once the index file has been read into the 
places array, the procedure called show is used to 
retrieve various descriptions and show them to the 
player. The key to the operation of show is the 
following loop: 


seek 


(xfile, 


2) 8 


get (xfile)s 


places Cloc] := xfile™s; 
REFEAT 
(loc); 


loc 8 succ 


get (xfile); 


r= xfile‘; 


placesfloc] 


UNTIL loc = 


flames; 


The interpretation of this is simple, given what you 
know about the descsection records. The show 
procedure starts at dbegin for a given location and 
goes to dend. For each value of i between those two 
numbers, there is a line of description in narrate. 
The show procedure calls seek to position itself to 
the correct place in the narrate file; then it calls get 
to retrieve the line of description, and finally it calls 
write to display the line on the screen. 

This for loop uses dbegin and dend directly. 
Actually these are abbreviations for: 


places[where].dbegin, and 
places[where].dend. 


The with statement that surrounds the for loop is a 
Pascal method for abbreviating references to com- 
ponents of records. If you say 


WITH places[where] 


you can refer to any of the fields of the record 
represented by places[where] without repeating 
the prefix places[where]. This is not only a way of 
saving the programmer some typing. It also allows 
the Pascal compiler to generate better code for 
accessing the corresponding record fields. 


THE BROWSE PROGRAM 
FOR PREVIEWING DATABASES 

In Chapter 19 I presented the listing of 
BROWSE. BROWSE is a simple program, similar 
to MAKEDESC, which you can use to examine 
adventure game databases that you have generated 


with MAKEDESC. BROWSE allows you to ask for 
locations by placename, then it displays the corre- 


sponding description from the data file generated by 
MAKEDESC. It has a convenience feature for 
those situations when you can’t remember the exact 
names of the locations in your adventure database. 
If you type ? as the name of a place to be described, 
the BROWSE program will display the names of all 
the locations in the database you are examining. 


181 


Programming 
Techniques Used in MAKEDESC 


In this chapter I delve into the programming 
techniques used in MAKEDESC. I discuss the fol- 
lowing ideas: 


@ Symbol tables (information gathering and stor- 
age). 

@ Hashing (hash tables, hash values, and so on). 

m@ Symbol table lookup using both linear search 
techniques and hashing. 


These ideas are taken from computer science. They 
may be a bit technical for some readers. However, 
you don’t need to understand them in order to use 
the MAKEDESC program itself. If all you wish to 
do is use descriptions databases in your own ad- 
ventures, you can probably skip this chapter en- 
tirely. On the other hand, if you make your way 
through this material, you will have some valuable 
programming techniques that will serve you in 
many other situations as well. 


SYMBOL TABLES 


The term symbol table is usually encountered in 
discussions of programs like assemblers and com- 


182 


pilers. Symbol tables keep track of information 
about source programs that compilers or as- 
semblers process. The UCSD Pascal compiler has a 
symbol table that, among other things, keeps track 
of the identifiers declared in a Pascal program. 
MAKEDESC is a much simpler program than a 
compiler or assembler, but it nonetheless uses a 
symbol table. The symbol table in MAKEDESC 
keeps track of placenames and information as- 
sociated with placenames that is needed for build- 
ing the descriptions database. 

The information kept in the MAKEDESC 
symbol table has already been described in the 
previous chapter. In fact, a large part of the 
MAKEDESC symbol table is a copy of the database 
index file. The additional component of the 
MAKEDESC symbol table is an auxiliary array 
called the hash table. I shall discuss the concept and 
usage of hash tables in detail below. 


The MAKEDESC Symbol Table: 
Functional Requirements 


In order to understand how the symbol table 


works, it is useful to know what operations it needs 
to perform. The operations required can dictate the 
programming techniques utilized. In MAKEDESC 
the following tasks must be performed: 


@ The addition of a placename to the symbol table. 

@ The addition of information about a placename to 
the entry for that name; for example, when the 
description of the placename is completely pro- 
cessed, the dend value is added to its entry. 
(Recall that dend represents the record number 
of the last line of description of the placename. ) 


m The looking up of a placename in the symbol table. 


This action occurs more than once. It occurs first 
when the name is placed in the table to begin 
with. At that time, the lookup operation fails. It 
occurs again if the same placename is used later. 
Then the lookup operation will succeed, and 
MAKEDESC will warn the user that a duplicate 
placename has been detected. The lookup opera- 
tion is also part of the add-a-placename-to-the- 
symbol-table operation. The lookup is performed 
in order to tell MAKEDESC where to put the new 
placename entry. 


LOOKUP TECHNIQUES: THE LINEAR SEARCH 


I have already discussed the technique of 
linear searching and the variation on that technique 
that uses a sentinel to simplify the search. The 
linear search has its advantages and its disadvan- 
tages. 


Advantages of Linear Searches 

Linear searching is a very simple technique. It 
is easy to understand and easy to implement. Most 
programmers have used this technique in one fash- 
ion or another. The code for a linear search is quite 
simple and easy to remember. 


Disadvantages of Linear Searches 


Linear searching is the most inefficient of all 
possible search techniques. That is its main disad- 
vantage. If there are N items that must be searched 
through, on the average it will take N/2 attempts to 
locate what is being searched for. If the item being 


searched for is not present (which is the case most 
of the time in MAKEDESC), all N items in the table 
have to be examined in order to determine that the 
search has failed. 

As the number of items being searched 
through increases, the disadvantages of linear 
searching begin to outweigh its advantages. In the 
case of MAKEDESC, as the number of placenames 
in the adventure database increase, the inefficiency 
of searching them linearly increases proportionally. 
Because the number of placenames allowed by 
MAKEDESC is limited to 255, this would never be 
an intolerable burden. However, it was decided 
when MAKEDESC was implemented to use a more 
efficient search technique. 


HASHING: A MORE EFFICIENT SEARCH TECHNIQUE 


What is needed is a way to narrow the search 
through the symbol table. If you could put a limit on 
the number of entries that need to be considered 
during each lookup, this would increase the effi- 
ciency of the lookup process. The technique of 
hashing provides just such a method. The hashing 
technique uses an auxiliary array called the hash 
table. The hash table allows the placenames to be 
divided into anumber of smaller groups. For a given 
placename, only the placenames in one of the 
groups need be searched rather than all the 
placenames present in the table. The smaller 
groups are organized into what are called linked 
lists. Each linked list consists of only a fraction of all 
placenames. Each linked list is searched using a 
linear search with a sentinel. The linear search of 
the much smaller list of placenames is acceptably 
efficient compared to a linear search of all the 
placenames. In order to understand the hashing 
technique, you need to understand several concepts 
and techniques: 


@ The idea of a hash value. 

@ The computation of hash values. 

@ The use of hash values to determine a smaller 
list of placenames. 

@ The organization of the small lists using links. 


I shall discuss these ideas in the remainder of this 
section. 


183 


The Idea of a Hash Value 


A hash value is a number. It is anumber that is 
associated with a string of characters such as an 
identifier in a Pascal program or a placename in the 
source for a MAKEDESC database. Such numbers 
must satisfy some simple requirements: 


1. In a given application, the hash values are lim- 
ited to a certain set of values. 

2. For simplicity, the smallest hash value is usually 
taken to be 0. This is because hash values are 
used as index values for the array known as the 
hash table. 

3. The largest hash value is usually taken to be a 
prime number. This usually means that all the 
identifiers under consideration will be divided 
into relatively equal sized lists. This is clearly 
desirable, because the smaller lists are being 
searched using linear search. It would be self- 
defeating if one of the lists was quite large and all 
the others only one or two elements long. 


The Computation of Hash Values 


Given a character string, such as a MAKE- 
DESC placename, the hash value of that string must 
be computed. The technique used in this computa- 
tion is usually referred to as a hash function. The 
choice of a good hash function is an entire technical 
subject in itself and much has been written about it. 
I shall not dwell on details of that choice, but merely 
present one common solution. In MAKEDESC 
hash values are computed using the ORD and MOD 
functions from Pascal. The method is as follows: 


To form the hash value of placename s, con- 
sider the individual characters s[i] of s. Each 
such character has an internal number that may 
be computed in Pascal by applying the ORD 
function: 


internal value of s[i] = ORD (s[i]) 
If you added all those values (for each charac- 


ter in S, that is for i = 1 to LENGTH (s)), you 
would obtain a number, hv. The possible val- 


184 


ues for that number would be quite large, 
however. You want the hash value of s to range 
from 0 to some fairly small number. In 
MAKEDESC, the upper limit for hash values 
is 30. In order to produce such a value from the 
sum, hv, just described, you would use the 
Pascal MOD function. The MOD function pro- 
duces the remainder of one number when it is 
divided by another number. The two numbers 
in question here are the sum described above 
and the number 31 (a prime number, as men- 
tioned above). Thus, the final algorithm is as 
follows: 


symhash := 0; 

FOR i := 1 TO length (placename) 
DO 

symhash := (symhash + ORD 
(placename[i])) MOD (hashmax + 1) 


{ END DO }; 


The Use of Hash Values to 
Determine a Smaller List of Placenames 


Once the hash value of a given placename has 
been computed, how is it used? The hash value is 
used as an index into the array known as the hash 
table. Each entry in the hash table determines the 
beginning of one of the smaller lists of placenames. 
The characteristics of that list are as follows: 


w@ Every placename on the list has the same hash 
value. 

@ The list is arranged in reverse order, that is, the 
first name on the list was the last name added to 
the list. 


The numbers stored in the hash table array are also 
index values but they index into the places array. 
The places array is the array that is written out to 
the descriptions index file. The hashing technique 
now allows you to think that the descriptions index 
file consists of a number of short lists. Each hash 
table entry “points” to the beginning of one of these 
lists. This is illustrated in Fig. 23-1. 


hash [i] 


Fig. 23-1. Hash table entries. 


The Organization of the Small Lists Using Links 

Each entry in the places array is a record con- 
taining information about one placename. The last 
item in each record is called the link field. This is 
the index (in the places array) of the next placename 
that has the same hash value. This piece of infor- 
mation is called a link, because it determines the 
next item on a linked list of items. That item is 
located at some possibly remote place in the places 
array, rather than being the next sequential entry in 
that array. The link field tells the lookup process 
how to proceed when it fails to find a given 
placename at the current entry. 

Figure 23-2 shows the concept of the link field 
pictorially. Within the large array, places, a given 


places [j] 


placename entry “points” to another by means of its 
link field. 

To summarize the concepts of hashing, let us 
consider an analogy. Think of the world headquar- 
ters of a large corporation. People within this cor- 
poration work in certain locations in that building. If 
you set out to find a certain employee by searching 
through every office in the building in order, you 
might be occupied for quite a long time. On the 
other hand, if you started with the knowledge of 
what department the employee you sought worked 
in, your search would be considerably narrowed. 

The linked lists of elements all with the same 
hash value may be thought of as analogous to a 
department in a large corporation. They all have 


185 


hash [i] 


places [j] 


Last link: 


All hash chains terminate 
at places [0] which acts 
as a common sentinel. 


Fig. 23-2. The linked lists or “hash chains” in the places array. 


something in common: in the corporation it is their 
department number; in the hash search technique it 
is their hash value. Searching a linked list of ele- 
ments is like searching through just the offices 
within a given department. There are far fewer 
offices in a single department than in the entire 
corporation. In the same manner, there are far 
fewer elements on a single hash list than there are 
in the entire symbol table (or in our case the entire 
places array). 


186 


THE HASH TABLE LOOKUP TECHNIQUE 

I conclude this chapter by reviewing the actual 
algorithm used in the hash table lookup technique. 
That is, once a hash value has been determined, 
how is the linked list of items with that hash value 
searched? 

In MAKEDESC, I have used the entry 
places[O] as a sentinel to mark the end of every 
linked list in the hashing scheme. The hash table 
itself is set up to contain all zeros to begin with. 


This means that all the linked lists are empty. This 
is the situation before any placename descriptions 
have been read from the MAKEDESC source file. 
This is illustrated in Fig. 23-3. 


Adding Placenames to the MAKEDESC Symbol Table 


As MAKEDESC reads placenames from its 
source file (by reading placename instruction 
lines—see Chapter 20), it enters them into its 
hashed symbol table. The process of entering the 
next name is:as follows: 


@ First the hash value of the new placename is 
computed. 

@ The hash value of the placename is used as an 
index into the hash array. The number stored 
there determines the index of an entry in the 
places array. If the number found is 0, there are 
no placenames yet with the given hash value, and 
an entry for the placename is created. How the 
new entry is created and added to the table is 
described below. 

g@ If the number found in hash is not 0, there are 


already placenames in the symbol table with the 
same hash value as the current placename. The 
linked list of those hash values must be searched 
to eliminate the possibility that the current 
placename is a duplicate. Here is the process 
used: 


1. Store the current placename in the name 
field of places[0]. This is the sentinel con- 
cept in operation: the record in places[0] is 
at the end of all the linked lists in the sym- 
bol table. The lookup process will eventu- 
ally reach the entry if the placename under 
consideration is not found earlier. The 
statement 


places[0].name = thisname; 
accomplishes this. The statement 
where := hash[symhash]; 


takes the value out of the hash table to 


places 


Fig. 23-3. The initial configuration of the hash table: all hash chains consist of only the sentinel location (places[0]). 


187 


places 


nextplace 
Situation before entry of 
new place in places table: 


symhash = 2 
hash [symhash]= 4 
nextplace= 17 


hash [symhash] := 
nextplace; 


=> 


Adjusting the 
hash chain 


Fig. 23-4. A diagram of the process of making an entry in the places array. 


188 


determine the head of the linked list to be 
searched. 

2. Use the following Pascal while loop to 
search the linked list of places with the 
same hash value as the current placename: 


WHILE places[where].name < > 
thisname 
DO 
where := places[where].link 
{ END DO }; 


At the end of this loop, the variable where deter- 
mines whether or not the search has succeeded. If 
where contains the value 0, the search proceeded 
all the way to the sentinel and the lookup failed. 
Otherwise, the search succeeded and where con- 


tains the index of the entry that duplicated this- 
name. 


Adding an Entry to the Places Table 


If a given placename is determined not to exist 
in the places array, it is added by MAKEDESC. 
The new entry must be placed into the linked list of 
names with the same hash value. The variable 
nextplace always contains the index of the next 
available spot in the places array. This variable is 
initially set to 0. It is then increased every time a 
new placename is added to the symbol table. In 
particular, it is set to 1 before the very first 
placename is added to the table. 

The process of adding the new place to the 
symbol table is accomplished by the entername 
procedure in MAKEDESC. Figure 23-4 illustrates 
the process pictorially. 


189 


What Else Can You Put on Disk? 


In the preceding four chapters I have discussed the 
MAKEDESC program used for creating a database 
of place descriptions on disk. In this brief postscript 
I speculate on what else you might want to or be 
able to put into disk files. 


OTHER DESCRIPTIONS 


The descriptions in the databases have so far 
been limited to those of adventure game locations. 
These are by far the most extensive kind of textual 
description that has been used in either of the sam- 
ple adventures so far. However, there are many 
other kinds of descriptions that are not descriptions 
of rooms or locations: 


@ Descriptions of events that occur during the 
game. For example, in Adventure 2 the ogre’s 
being awakened is accompanied by some de- 
scription. 

@ Descriptions of speech that can be either that of 
the guide in certain situations or that of other 
beings that might be encountered during the play 
of the game. 


190 


m Descriptions associated with objects that occur in 
the game. There might be extra information to 
convey about an object either by itself or under 
special circumstances. 

w@ Descriptions of places that are only given condi- 
tionally. The descriptions database as described 
so far is set up so that any time a location is 
visited, the description is the same. Examples of 
conditional descriptions in Adventure 2 are the 
various descriptions given as the player digs for 
the treasure. 


Some of the categories of descriptions in the 
above list can easily be accommodated within the 
current implementation of MAKEDESC and its as- 
sociated databases. I give examples in Adventure 3, 
which begins in Chapter 25. Other categories are 
not so easy to accommodate. One of the more dif- 
ficult is the conditional description idea as it relates 
to the current method of traveling. Consider the 
example of the ladder room in Adventure 2, where 
the response to “up” depends on whether or not the 
player is carrying the treasure. In order to accom- 


modate that conditionality, some additional argu- 
ments to travel might be needed, tied in somehow 
with the database. I leave it to you to speculate on 
this further. 


PUTTING THE WHOLE PROGRAM 
ON DISK: ADVENTURE INTERPRETERS 


A radical approach to writing adventure games 
is to first design an adventure machine. This would 
be an abstract machine whose only purpose was to 
run adventure games. The machine language of 
such a machine would be designed specifically for 
executing adventure games. Programs for the 
machine would be referred to as adventure code. 

A Pascal program, which emulated such an 
adventure machine could be written. The program 
could be designed in such a way that the entire 
adventure code program that it interpreted would 
“live” in various disk files. This would have the 
following consequences: 


@ The size of an adventure would be limited only by 
the size of file(s) that could be fit onto a floppy 
disk. In fact, given the ability to swap disks in and 
out at the behest of the emulator, there would be 


no limit. That would mean you could write very 
long adventures. 

m The play of the game might be slower. The 
adventure emulator would have to do more disk 
i/o and that would slow down response time. 
This might turn out to be tolerable or it might 
not, depending on the details of the implementa- 
tion of the adventure emulator. 

@ In addition to an adventure emulator, you would 
need another program that translated adventure 
games in some high-level form into adventure 
code. This would be an adventure compiler, if you 
will. The adventure compiler would process the 
specification of an adventure game in the adven- 
ture language. You would have to design the ad- 
venture language, making sure to include all the 
necessary features to support reasonable and in- 
teresting adventures. 


While all of this is quite interesting and fun to 
speculate on, I will not have room to expand upon it 
in this book. Others before me have already acted 
upon these ideas: the Scott Adams Adventures 
were implemented in somewhat this fashion; Zork 
and other adventures from Infocom, Ltd. were im- 
plemented using the adventure language concept. 


191 


Preview of Adventure 3 


Adventure 3 is the most ambitious adventure game 
in the book. Compared to the original adventure, it 
would still be rated as intermediate. However, 
compared to Adventure 1 it is quite advanced. 
There are more locations, more problems, better 
descriptions, and more challenges than there were 
in the previous examples. Much of the code is 
shared with previous adventures. The newest con- 
cept is the organization of the program into “units.” 


OUTLINES, DIAGRAMS, AND MAPS 


Because Adventure 3 is more complex than 
previous adventures, there are more figures as- 
sociated with it. Figures 25-1 through 25-6 present 
the code outlines of Adventure 3. Each UCSD unit 
used by the program is outlined, as well as the main 
program, whichuses the units. Figure 25-7 showsthe 
units of Adventure 3 and the uses relations between 
them. 

The map for Adventure 3 is large. I have di- 
vided it into several parts so that it will fit into the 
book’s page size. Figure 25-8 shows that the map of 


192 


Adventure 3 has six separate pages and also shows 
the offpage connections between them. Figures 
25-9 through 25-14 provide the map of Adventure 3 
and Figure 25-15 presents a summary of the prob- 
lems of Adventure 3. 


CHAPTER PREVIEWS 


Adventure 3 is described in Chapters 27 
through 29. The listing of Adventure 3 is in Chapter 
26. The three chapters do not repeat territory 
covered in the descriptions of earlier adventures. 
So if you find code that you think should be 
explained but isn’t, refer back to preceding sec- 
tions, and perhaps you will find it discussed there. 


Chapter 27 is entitled “Larger Programs: ' 
Using UCSD Units” and deals with the organiza- 
tional technique of program units. The UCSD ver- 
sion of Pascal provides a method of structuring 
called units. A unit is a self-contained part of a 
program containing two parts, an interface and an 
implementation. The interface of a unit contains all 


“yS!] SUN 94} 
“BYEPAPE JINN 40 auUlNO a4} °E aINUSAPY JO} BUI]JNO BpOd ay, ‘Z-Sz ‘B14 = Bulpnjou! auI|yNO WesBoid ay} :¢ aunjUsAPY 40} aUI]INO apod ay, *|-Sz “B14 


“ON3 $-Sz ainbi4 88g $90] 

9-z ainbi4 aes Lspwig 

“yun eyepApe au} ul paejoep ‘Tul ¢-S2 aunBi4 88g ZSpwg 
SO|QEWEA 94} JO} BpOO uOHeZI|eNIU] { +L Huy b-Sz ainbi4 aes espwg 
€-Sz¢ ainBi4 8ag sqolg 

NI93a 2-Sz ainBi4 eeg = Bye pApy 


‘Zul IUNSIIONd 


; (JUdA 
‘yyul 
Luu! 3YNd3I0Nd -peyw UN aas) WOGNYY uolouny e 


NOLLWINAW3 Tawi apiaoid JSNW :AYWHEIT'WALSAS Woy umsaiddy 3s!) swup 


00} ainpacoid,, ay} plore 0} Japs0 


{ 
ul pasn sainpasoid uolyezieN!u| 


‘€ BINJUBAPY Ul S}JUdAB aqissod Huljussaidai SajqeeA UBB|OOg 
auop ‘payiy ‘uayea ‘uados! ‘eppemples ‘SaAl|i} 
aweulgo 
c SINJUSAPY Ul SY “paysia 
‘alaysyeym 
‘aueb ay} Bulunp pasn saipooB jo sjas snowea 
YSE}s ‘SOyeJWWM ‘SEYWWM ‘S}JUEMUWUM 
‘yeadde Aew Sjj01} 94} YOIYM JE SWUOOI $0 JOS - $904]01} 
Z pue | SainjuaApy ul Se - UON}e90) 


‘juaweye}s 

aseo ajbuis e AjjeoiBo) si yeyM yNO Aueo 0} (Z} pue 4}) 
$aJNP8901d OM) SOXOAU! }! ‘SY9O|Q BPD GSON 0} Ww] azis 
e JO asnedag ‘SuO!ed0] UdaMjaq |aARJ) Sa|PUeY a1ay BpoD 


‘wesGoud ulew ay} ul pasejoap 
UWA suoljouny pue Sainpaoid ay} yo Aue ul 10 8/84 paxOAu! 
Z ainjuaapy ul se - uoN9a]}09 aq Aew wesBoid ay) Aq pasn yun Aue jo saoepajul 
“WU 0} Saa}U! OU jo are inq ay} Ul pase|Dap SUOI}OUN) pue SaiNpad01g ‘swesBoOId 
SaiNSe9J} ay] Waeas yey) $]9aIGo sjuasaidai sayey ‘ajdwexa 40) :s}oa!qo Jo [BOSE JOY}O Ul SB '}Suly PAYXOAU! S| YOOIG WesBoud UE ay L 
Aiobajyeo jusiayip e sjuasaiday yoea—adA} saipoos au} jo sabueiqns NID3a 
Apoodwwm ‘yses} ‘sayey 
‘€ ainjuaApy u! S}oalgo |e Buyuasaidas adA} page oe suonouny pue ‘sainpasod 
‘adA} sasap ay} jo abuegns e si adj} swioos ay Suonese|29P HWA “AdAL “LSNOD 
‘€ SINJUBAPY U! SUOHEDO} au} BuljUasaidas adA} payesowinuy , : 
swool Sl} AYVYEITWALSAS ‘so0| {apoo'soojng 
“‘€ ainjueapy Aq pasn suoinduosap |je Buueseidas adA} payeuownuy ; 84} Ul Aq JSNW SJ9Y}O |IV ‘tspwo {apoo';spuiong 
sosap JUBWWOD ay} Ul Palyloads ‘zspwa {apoa:zspuiang 
Sl} BPO BU} Ul PUNO) ae ‘espwia {apoo'gespuiong 
AdAL yt ng} ‘sqoud {apoo’sqoudng 
WO} BY} JO SJUBWWOD Aq ‘eyepape {apoo: | yung } 
30VINALNI paepeoeid esoy, ‘wesbod ‘ymsajdde s3sn 
ayy Aq pasn syun jo ysI7 


eyepape LINN quaApeyw WVYIONd 


‘ESP LINN JO euIRNO ayy :E eiNUeAPY 404 aUIIINO apod aUL “p-Sz “Bly 


"ESPWd U! Sa|qUeA au} JO} BPOO UOHEZHeENIUT 
NI93¢ 
‘suolouny pue Saunpasoid soepaju! ay} Buljuawajdw! apo 
*‘suonduosap algo jo wewwes6 au} 


aAoiduwu! 0} saipoobmoys Aq pasn - saiqewea saipoob 40 14S 
sueasn ‘sawos ‘sjeinjd 


UVA 
NOILVLNIW31dWI 


*UB9|00g:(S91p003:}!)sa1poo3¥9 NOILONNA 


Se See ‘sa1poo8:dnyoo}iqo NOILONN 


Z ainjuanpy u! sv *(S9Sap asym) MOYS JYNGIIONd 


‘yoojd 34NdII0Ud 
( 


(s}aafqomoys <+ saipoosmoys) { ‘saipoo3 mMoys 3YNd3II0Ud 


90|3y9 
aweupwo 
112) 


peay 
& ainUsApY U! SV puewwoo 


saoejd 
ayeueu 
OY yX 


UWA 


dasa0e|d 
aul|A10}s 


Z ainjuanpy Ul SV aweud 
spwo 
suonoasip 


3dAL 


3OVIUIINI 
‘espwo LINN 


“sqoud LINN JO eul|NO ay} :E auNjUeAPY 40} OUIIINO apod ay ‘E- 


“ON3 
NIS3a 


“yun sqoid ay) Aq pauinbas uonezieniul on { 


“Anoaaip 

suoissasdxe au} Bulyenjead uey) sayyes ‘UOOUNY 

SIY} 0} S|}ed BSN aunjuUsApe ay} JO SWed J9YyIO 

“sjuauodwoo wWajqoid 0} Bulpuodseu0o suoissaidxe 

Uuea|O0g ay} JO UOHEN|eAS au} 9Z1/e4]UBd 0} UO!}OUNY 
‘ueajoog :(swajqoid :yoIym) Paros NOILONNS 


‘ad Ay Swiayqosd 94} YYIM 4OI[JUOD PIOAL 0} 

sqoid peweu Ss! jas}! uN ay, ‘Panjos si pajuasal 

-dai wajqoid ay) asojaq ans) aq ysnw yeu} 

uoissaidxe ueajoog e 0} Spuodsai0o adA} swajqoid 

8u} Ul JayIJUBP! YORI *E ainjUsApY Ul SWa|qoid ay} 40 

s}uauodwod ay} Bujuasaida ad/} payessuinug 
swajqoid 


3dAL 


JIVINILNI 


‘sqoud LINN 


194 


"LSPWO LINN JO eUINO ay} :E eiNyUeApY 40} BUI]NO epod a4 *9-GZ ‘B14 ——*S90}_ LINN PUB ZSPWO LINN JO eUI|NO ay} :¢ eiNjUeApy 10) aUIIINO apod eu “S-Sz ‘B14 


“ONI 


“o's, ‘u,] =? sueyop “uolyeo0} S,WWM 
OY} PaysIA sey JaAejd ay) SAWN JO JaqUUNU ay} JUNOD oO} BiqeUe/A 
NI938 ‘W293LNI ‘wwaye 
“‘Seinpadoid soepajul ay} JUdWA|dWI 0} apOD VA 
*suonoauip :AeMYSIYM NOILONNA 
‘UVHO :puewWwos0P NOILONNA NOILVLN3W31dWI 


Z ainquaapy ul sy somml auNd3I0ud ‘UONedO] S,uIeJUNOW ay} Jo 
‘sa8aqut :asoosd NOILONNA UeW ASIM ay} Je UOOe ay} a|pueY Oo} ainpadolg 
‘spwo :dnyoojpwd NOILONN 


‘wud 34N0390Ud 


"S|JO4} BY} JO SUOI}Oe pue snjejs 
ay} SuoluOW swajqosdyd ‘VeEinoed f ‘uoNDe}01) 3yNGID0Nd JOVINILNI 
u] “€ auNjUeApY JO SWa;qoid 3y} *y@9u}01) JYNGIIONd a 
yo yoddns ul pesn ainpesoid ayeaug | ‘Swaiqosdy9 34NG3I0dd 


"SPUBWWOD UO!DAJIP 0} puodsaJ09 
yeu) Siay9| Bunueseidas— 13S YWHO ‘YWH9 4O 13S ‘sieyop 
UWA 


NOILVLNSW31dWI 


“J@YJOUL 0} UOIEO| — 
QUO WO4} |@ABJ} PUB UONe}UBWa|dW! PUBWWOD 10} SaiNpad01q 


‘eppemd 3ungag0ud 


‘djay 10} payse sey saAejd ay} saw Auew Moy yo ye) deay 0} aiqeueA 
‘ynoysd JUNd3IONd ‘Y3931NI :payse 
‘uadod 3ud390Ud 


‘doupd 34nd390ud UWA 


*Aueod 34Nd390Nd 
*(SW00s:90)p'90jN‘90jM‘90]9‘D0}S‘I0|U) }aAes} JYNDIIONd NOILVIN3W3IdNI 


J0VINILNI ‘djayd 3uNG390Nd 


‘spud LINN JOVIUSLNI 


‘wspwso LINN 


195 


‘wey} UseMjeq 


suoldauuo0d abedyo pue ¢ ainjusApy 410} Sebed dew Buimous wesbeip y “g-sz ‘bi4 


€1-S2 aunBi4 
dew uyyi4 


6-Sz eunBi4 
dew jsul4 


vi-s2 eunBiy 
Ol-S2 eunBbiyg dew ujxis 
dew puovas 


1-2 eunBi4 
dew pays 


Zi-Sz2 aunbi4 
dew yuno4 


"€ SINUSAPY JO SHUN ay} JO} SUOHeja1 SeSN ey “Z-Sz ‘B14 


yun 
eyepape 


JUSAPe}W 
swesBoldg ule 


196 


cellar 


fountain 
(of youth) 


Fig. 25-9. The map of Adventure 3: Page 1. 


197 


Fig. 25-10. The map of Adventure 3: Page 2. 


the information that the unit provides to other parts 
of the program. The implementation contains the 
code for all procedures and functions of the unit. 
The advantage of the unit concept lies in the fact 
that any part of the implementation may be changed 
without requiring the remainder of the program to 
be recompiled. Also, other parts of the program 
cannot use knowledge of the innards of a unit in 
order to play programming “tricks.” This makes for 
more robust code and fewer bugs. Chapter 27 ex- 


198 


plains the syntax of units and how to use them in 
your adventure game programs. It explains the 
interface and implementation concepts and talks 
about linking units together to form a program. 
Units are separately compiled from one another, 
and so there is an extra step in preparing a program 
that uses units. This step is called linking. 
Chapter 28 delves into the problems im- 
plemented in Adventure 3. It explains how a large 
part of the representation of those problems is en- 


carvings 
berries 


saddle1 


cavela 


Fig. 25-11. The map of Adventure 3: Page 3. 


199 


"g aBeg :€ ainjuanpy jo dew ey ‘e1-Sz “B14 


‘y aBq :€ ainquarpy jo dew ay) ‘Z1-Sz “Bly 


a) 
s ‘p ze|ppes ae 


M 
n 


pis 


200 


"€ eunjuaapy jo Swaiqoud ay “S1-Sz ‘B14 ‘9 eBey :€ ainjuaapy jo dew aut “y1-Sz “614 


‘dn pauado si uinoA jo urejunoy 

94} 0} Aem au} ‘op noA ua ‘PUBWWOD eB 

se }| 8SN puke JeIj99 ay} 0} LUN}aJ SNW NOA 
‘no 0} puom a1Bew ay} sjeanes WWUM 9u} Jay 


‘puom o16ew au} 

JE9Ae1 WLUM au} ||IM Ud} AJUO pue Udy “p 
wajqoid Burajos saye uoled0) SWUM ay} WO 
.YSeJ},, Je pue Saye}, |e SPAOWSI JsnW NOA 


‘g wajqoid Buiajos saye 

—B | aAe9d UOIe90} Je Y}OO} Sj}04} 84} Bulpuy 
sapnjoul Siu, “Wwauy} sAea}| pue WWM UO!}ED0} 
0} ,, SaipoobuwM,, au} je Bug ysnw no, 
“‘souisap Ajns} WWM ay} yey} $}9alqo asou} 
wod4 Sainsead} ,,ayej,, ysinBuljsip snus Nod 


“9ABD BU} 0} Hulpes} 
yoyey ay} uddo 0} JAapsO Ul JeI}9O By} 
0} way} Aveo pue shay ay} aye90) JsNnW NO, 


“JOABIOY 
S|]04} 84} JO Pld aq UBD NOA ‘sauog au} 
doup noA ji uayy “e} BAD O} Sj]O1} BY} 

aun} 0} Way} asN ysnwW NO, ‘wWayY} Ae 
pue a8Aed 9y} Ul S9U0g 9y} pul} JSnW NO, 


“UO1}EDO} S|]O1} BU} Ye BAUIe NOA UayM Sau0g 
ay} BurAueo aq jsnw no yeu} Sueaw siy, 
*$|]04} a4} Aq uayea Bulag plone ysnw No, 


201 


capsulated in the program unit known as the probs 
unit. The use of the solved function is discussed in 
connection with this. The problems posed by the 
adventure and the Pascal code used to implement 
them are described. The precondition—post- 


202 


condition notation is used as it was in the descrip- 
tion of Adventure 2. 

Chapter 29 discusses the miscellaneous cod- 
ing techniques used in the implementation of Ad- 
venture 3. 


Adventure 3’s description database file 
can be found in Appendix C, page 292. 


The Listings of Adventure 3 


This chapter presents the listings of Adventure 3. units used in the program, as well as the main 
There is a separate listing for each of the Pascal program. 


LISTING 26-1. ADVENTURE 3 MAIN PROGRAM = Save file as “MTADVENT”, as per page 243 
PROGRAM mtadvents 


C$s+3 
USES applestuff, These are “uses” instructions; see Chapter 27, 
{tumti.code} advdata, specifically page 242 for more information. 
{$uprobs.code? probs, aA , ; 
{$ucmds3.code} cmds%, For these and additional “uses” instructions 
{$ucmds2. code? cmds2. which appear on pages 207, 209, 224, 225, 233 
{$ucmdsi.code} cmdsi, and 234, refer to “A note about disk Drive 
{$ulocs.code} locs; numbers in file names and commands’on page 3 
of this PDF. 
PROCEDURE t1 (ll: rooms); 
REGIN 
CASE 11 OF 
starts: travel (traili, trail4, nowhere, 


203 


204 


trailis 


trails: 


trails: 


trail4: 


trailts: 


trailé: 


coli: 


cola: 


cols: 


col4: 


rubblels 


rubble?s: 


mound : 


travel 


travel 


travel 


travel 


travel 


travel 


travel 


travel 


travel 


travel 


travel 


travel 


travel 


monastery:travel 


cellars: 
Caver 
slopels 
slopes 
slopes: 
mut ds 
ruts 


ruts 


travel 


travel 


travel 


travel 


travel 


travel 


travel 


travel 


trail3, nowhere, nowhere) s 
{frubblel, start, nowhere, 
nowhere, nowhere, nowhere) 3 
(trailé, rubble2, nawhere, 
trail&, trailé, rubblel)s; 
(nowhere, nowhere, start, 
slopes, nowhere, nowhere): 
(start, mound, nowhere, 
nowhere, nowhere, nowhere) 3 
(nowhere, nowhere, trail2, 
saddlel, saddlel, nowhere); 
(saddle3S, trail2, nowhere, 
nowhere, saddlest, trail2); 
(nowhere, nowhere, saddlel, 
nowhere, saddle2, saddlel); 
{(slopeS, rut3, nowhere, 
nowhere, slopes, rut); 
(nowhere, nowhere, steeps, 
nowhere, peaki, hilll)ds; 
(nowhere, guillyl, nowhere, 
saddlez, nowhere, quilyl)s; 
(trail2, traili, rubblez, 
nowhere, trail2, nowhere); 
(nowhere, nowhere, nowhere, 
rubblel, nowhere, nowhere) s 
(trail4, nowhere, nowhere, 
nowhere, nowhere. monastery) ; 
(nowhere, nowhere, nowhere, 
nowhere, mound, cellar); 
(nowhere, nowhere, nowhere, 
nowhere, monastery, Cave); 
(nowhere, noawhere, nowhere, 
nowhere, cellar, nowhere) ; 
(nowhere, saddle2, nowhere, 
nowhere, steepl, saddle2)s; 
(nowhere, nowhere, mowhere, 
nowhere, steeps, trolls); 
(steeps, col2, trails, 
chasm, steeps, noawhere) : 
(nowhere, nowhere, ridge, 
trolls, nowhere, nowhere) 
(nowhere, nmowhere, ridqgel, 
nowhere, nawhere, nawhere) § 
(col2, nowhere, nowhere, 


nowhere, 


cole, 


nowhere) § 


END {¢ CASE 11 OF } 


22 


END { PROCEDURE t1 33 


PROCEDURE t2 (ll: rooms); 


BEGIN 
CASE 11 OF 
steepl: travel (nowhere, nowhere, chasm, 
nowhere, wmm, slopel)s; 
steep2: travel (nowhere, nowhere, chasm, 
nowhere, peak2, slope2)s 
steep3s: travel (nowhere, nowhere, nowhere, 
chasm, peaks, mtcave2c)s 
steep4: travel (nowhere, nowhere, chasm, 
nowhere, peak4, ridgel)s 
steept: travel (peakS, slopes, nowhere, 
chasm, peakS, slopes); 
peaki: travel (nowhere, nowhere, nowhere, 
chasm, nowhere, col); 
peak2: travel (nowhere, steep2, chasm, 
nowhere, nowhere, mtcavelc)s 
peaks: travel (nowhere, nowhere, nowhere, 
chasm, nowhere, steep): 
peak4: travel (nowhere, chasm, nowhere, 
nowhere, nowhere, steep4); 
peakS:s travel (nowhere, nowhere, chasm, 


nowhere, nowhere, steepsS) : 
saddleil: travel (trolls, nowhere, rutl, 
coli, trolls, mtcavela);: 
saddle2: travel (slopel, coll, steep2, 
ridgel, slopel, coll); 
saddle: travel (nowhere, trailéd, col4, 
nowhere, hHilli, traild)s 
mtcavela: travel (mtcavelb, nowhere, nowhere, 
nowhere, trailS, nowhere) ; 
mtcavelb: travel (mtcavelc, nowhere, nowhere, 
nowhere, nowhere, nowhere); 
mtcavelcs travel (nowhere, mtcavelb, nowhere, 
nowhere, peak2, nowhere); 
mtcaveZa: travel (mtcave2b, nowhere, nowhere, 
nowhere, qully2, nowhere) s 
mtcave2b: travel (mtcave2c, mtcave2Za, nowhere, 
nowhere, nowhere, nowhere) ; 


205 


MECAVELC:? 
gullyi: 
gully: 
hills 
trolls: 
ridqels 
ridge2: 


Wwinms 


chasms 


END ¢ 


END € PROCEDURE t2 33 


BEGIN 


writeln 
writeln 


show 


IF ord 
THEN 

ti 
ELSE 


t2 


travel (nowhere, mtcave2b, nowhere, 
nowhere, steeps, nawhere) 
travel (cal4, gully2, nowhere, 
nowhere, col4, gully2); 
travel (gullyl, nowhere, nowhere, 
nowhere, gqullyl, mtcaveta); 
travel (nawhere, nowhere, nowhere, 
nowhere, cols, saddles); 
travel (slope2, saddlel, chasm, 
nowhere, slope’, saddlel); 
travel (nowhere, nowhere, saddlez, 
rut2, steep4, nowhere) ; 
travel (nowhere, nowhere, saddle?, 
nowhere, nowhere, nowhere) ; 
REGIN 
pwmms 
travel (nowhere, nowhere, nowhere, 
nowhere, nowhere, steepl); 
END s 
BEGIN 
done := true: 
killed = trues; 
END: 


(location) 


(location) 


(location) 


{ END IF ord ... 


UNTIL done; 


206 


CASE location OF 3 


ie “Adventure” and “There” 


(‘adventure 3 begins..." )3 
(*there are 


repeat t 


visitedClacatioan] 
(location) ; 


*, memavail, °® bytes of memory 


:= visitedClocation] + 1s 


“ ord (steepl) 


* 
+s 


left. "); 


END. 
LISTING 26-2. LOCATION PROCEDURE UNIT 


Ds sus wa tn aS sss bn shen aks nsx ie nena Sw: HO Sih a tn hc one th Stns st un oie 
{ 

{ Il ec uiom of t 

if 

{ the locs unit cantains procedures needed for 
{ handling special situations at certain game 
{ lacations. in mtadvent the anly such place 
{ is “‘wmm". therefore, this implementation of 
{ locs cantains anly the procedure pwmm. 

if 

Lm sa scion sine shtin eenbe, Fae wots boeee ane: Sone es eer so ‘etononeeb seece. oped Stone nenee saben oosin enews aatis beens Stee Gente Seceh sitnh ene ceeed sneha Sniun Stans sense snens 
{$S+3 


UNIT locss 
INTERFACE 


USES applestuff, 
{tumtil.cade} advdata, 
{$uprobs.code} probs, 
{$ucmds3.code} cmdst, 
(S$ucmds2.code? cmdsez, 
{$ucmdsl.code} cmdsl; 


FROCEDURE pwmm; 


IMPLEMENTATION 


VAR 
atwmms INTEGERS: 
ae te met nee te me er nee ee se te tn st tne sn ee he at me mn ns eee se ena wen Sonn Goan tose abt es eee esane 


za 

= 

3 

3: 
wt bs es be 


location wmm. 


% 
£ 
{ handle special occurrences at the 
: 
4 
{ 


j 
he he he, 


Hy 
H 
i 
$ 
1 
H 
$ 


PROCEDURE pwmms; 
VAR 
rs INTEGER: 


RJ RS he OS RS RS he Re Re ke he 


207 


BEGIN 


atwmm 3: atwmm + 13 


IF solved (wmmhappy) 
THEN 

show (wmmbl ab) 
€ END IF 3; 


IF wmmcount 3 3 


THEN 


show (wmmharp) 
{ END IF 35 


rose rand (1,5); 


CASE r OF 


show (wmmspl); 
show (wmmsp2) 
show (wmmsp 2) s 
show (wmmsp4) ; 
show (wmmsps) 3 


Chie 


END < CASE r OF 33 
END { IF atwmm > 1 35 
IF atwmm = 1 
THEN 
show (wmmhella) 
€{ END IF 33 
END ¢ PROCEDURE pwmm 3}; 


BEGIN 


208 


LISTING 26-3. COMMANDS 1 UNIT 


source file: cmdsl.text 


he she ah 


for the name of the cammand, 


all located in this unit. 


Ar eer Ss eM ee ee I eR eT eS 


in the interface to cmdsil. 


re ee ee rn 


{$+} 
UNIT cmdsis 


INTERFACE 
USES applestuff, 
{fumti.coade? acdvdata, 
{$uprobs.code? probs, 
{$ucmds2.code? cmds2, 
{$ucmds3.code? cmdsis 


FROCEDURE travel ( 


nloc, 


this unit contains all command processing 
support pracedures and functions. each 
command in the adventure is carried aut by a 
procedure called ptcmd>, where 


AS 


player: @.qg. pcarry “===> carry command. 

same of the ptcmd> procedures are located in 
this unit, the others are in cmds2 and cmdss. 
procedures and functions like travel, noway, 
docommand, listen, and cmdlaokup which are 
invoked in order to recagnize the command are 


the procedures ckproblems, trolmeal, and 
trolaction are also located here in order to 
be accessible to the appropriate command 
processing code. none of these procedures is 


tcemd> stands 
typed by the 


Be wh oS ke ke be ks Le ts 


LS kw bs bet bh he ke 


BS tt het tt 


Ls ie 


j 
| 
| 
i 


cmds3 must be listed before cmds2: 
{Sucmds3.code} cmds3, 
{S$ucmds2.code} cmds2; 


209 


sloc, 
eloc, 
wloc, 
uloc, 
dlocs rooms); 


PROCEDURE pcarrys 
FROCEDURE pdrops 
FROCEDURE pshout; 
FROCEDURE popens 
PROCEDURE pwaddas 


IMPLEMENTATION 
VAR 
dchars: SET OF chars 
Cc k p r Q b 1 e m s 


see if the player has salved any problems 
because of the command just executed. 


et het et A ht Re 


ea ee we ot 


FROCEDURE ckproblems; 
BEGIN 


IF (solved (bonetrolls)) AND (salved (luretralls)) 
THEN 
BEGIN 


show (tgabbones) ; 


trlives := false; { make “em disappear } 
whatshere(Cmtcavelal := 
whatsherel(mtcavelal] - Cboanesd; 


END ¢ IF (solved (bonetrolls) ... 3 
IF (NOT trlives) AND (location <> mtcavela) 
THEN 
whatsherelmtcavelal] := 
whatshere(mtcavelal + Ctoothd; 


END ¢€ FROCEDURE ckproblems 33 


210 


{mer ce a me met es cee cate ee eect ce at an ee ne ee ste ee es tt ne ee te ne ss ey 
{ t r Q 1 m e a 1 3 
£ + 
{ print the description of the player } 
{ being done in by the trolls and } 
{ exit the program. 3 
{ sence soe wasen costa soene ene samme suees ene nnse saute cones ntees wumm sane sense meet snsen some Samee sent canes sess non ene teuse comme sense rem sous sanee cert onnte sone seme cece capes } 


PROCEDURE trolmeals; 
BEGIN 


show (trtalk)s 
exit (PROGRAM) 5 


END { PROCEDURE trolmeal 33 


£ 
t t r QO ] a Ce t i oO n 
{ 


{ handle the trolls and assaciated problems. 

{ detect the first encounter with the trolls, 

{ moniter the trolls following the player when 

{ the player is carrying the bones. check for 
luring the trolls to mtcavela. 


rs 
t 
Lasse sence sssse svsne sasen sopee coset suman acces cece seses sotse oesce coven ovum evens ovens seuss saeet ennee steen erste stats seme eeeee sures ceeee anent seete seman arate stame svees svete eeeee omnes satan mane sueee crane comme seers seuss sues cases stems seuee 
% 


FROCEDURE trolactian; 
BEGIN 


IF (location = trolls) AND (troltime = ©) 
{ first encounter 3+ 

THEN 

BEGIN 


IF bones IN stash 
THEN 
REGIN 
troltime := turns; 
{ solve luretrolls problem 3} 
show (trhungry)s 
END 
ELSE 
trolmeal 
{ END IF bones IN stash 33 


END ¢< IF (location ... } 


ws ht ht ted LS RS ee 


Ls bs 


211 


The condition “(location <> trolls)” was 


i aalvwd Cureteot te) probably added to prevent the message 


THEN 
IF -Cheeetrern— tre —AND— 


fhenen Ih atach’ Instead this condition causes the trolls to eat 


the player if they return to the “trolls” location 


—— fr Fol i Ge) while luring the trolls, which is confusing and 
ELSE makes the game unnecessarily difficult. 
trolmeal aa ; dacsiaeet 
? END TF (Cloeation .. 3 * Page 248 indicates that if the player is luring 
€ END IF solved (luretrolls) }; the trolls and hasn’t figured out how to 


dispatch them after 25 moves, the trolls will 
IF (location = mtcavela) AND 


ecived tluretrolia) AHD code to handle this; instead the trolls disappear 
(hanes Th wean) and the game becomes unsolvable. One 
THEN solution is to add code here which checks 
show (tgablure) IF (troltime<>0) AND ((turns-troltime)>24) 
€ END IF 33 THEN kill the player. (On the included disk 
e —% image, | wrote code to display a short text 
END { PROCEDURE trolaction 3: passage, which segues into “trolmeal” which 
‘ displays more text and kills the player.) 
men mae me eee me tm mane tse se me i et en ne re tn aes en nt ens se te "Ye 
t c m d 1 Q Qo k u p 
{ determine which cammand the player has typed } 
{ and dissect the cammand string into *head’ 3 
{ and “tail’ far cammand verbs with abjects. 3 
[Tite sttee sees mun tate eee sno mate stim ate pvme see nn cena ett cutee ete ate tne sean van teen ant see sat tim ste ma sent sate tens sees thee Stns suet sens vente eee ota snes oe tute Meat snes ana eee } 


FUNCTION cmdlogkup : cmdsy; 


VAR 
p: INTEGER: 
lemd: cmdss 
BEGIN 


writelns Capitalize ‘Your’: 
write ( yaur wish is my cammand> *)3; 
readin (command) ; 


po r= pos ¢(* *, command): 


cr 


€ check for verb-object } 
IF p = © 

THEN 

BEGIN 


head := command; 


212 


“trfollow” from being printed immediately after 
“trhungry”, but it’s not needed if that’s the case. 


“have some fun” (eat the player.) There isn’t any 


END 
ELSE 
REGIN 
head == copy (cammand, 1, pri); 
tail == copy (command, pti, length(command) —-p); 


cmdnamelnocmd] : 
lemd : 


WHILE head <> cmdnamelClemd] DO 
lemd := suce (lemd) 
{ END DO 3, 


cemdloakup #= lemds; 


END ¢ FUNCTION cmdlookup 3; 


Pp S c o r & 


calculate the number of points scored by the 
up to this point in the game. 


a ae oe i eS a 
ee ed 


FUNCTION pscore : INTEGER; 


VAR 
re rCMaMms § 
keepscore: INTEGER; 
BEGIN 


keepscare = Of 


FOR ros start TO wmm 
DQ 
IF visited(irj = 0 
THEN 
keepscore := keepscare + Sy 


IF saidwadda 
THEN 


213 


keepscare 


IF location = 
THEN 
keepscore 


pscore := keepscore; 


END { FUNCTION score 


: 
3s 


i 
j 
I 
{ 
i 
| 
| 
1 
i 
H 
i 
i 
i 
! 
j 
H 
i 
i 
! 
i 
' 
I 


ei 
i 
-. 


& 


procedures 


me cee es es 


PROCEDURE listens; 
VAR 
lemd: cmds; 
FROCEDURE Ilscores 
BEGIN 
writeln 


writeln (pscore, 


END {¢ PROCEDURE Ilscore 


FROCEDURE lquits 
VAR 
chs CHAR: 
fhold response 
BEGIN 
writeln 
readin (ch) 4 
IF (ch = *y*) 
THEN 
BEGIN 
writeln 
exit (FROGRAM) ; 
END {IF (ch = “y") 
END { PROCEDURE lquit 


OR 


BEGIN 


214 


fountain 


or 


keepscore 


d 


keepscore + 


(S50 - 


(fountain) 


+ 
bh 
mi) 


dispatch calls to the command executian 
( ptemd?> as described in the 
header comment for this unit.) 


Capitalize where indicated by arrows 
("if you should quit now, 


your” 


- 24) 5 


wo ks 


Le ed 


Rs hw bet 


score 


points of a possible 350. 
350 isn’t the highest possible score, 
but I’m not sure what the correct number would be 


(ch 


+ 
2 


a8 
y 
35 


3 


for quit confirmatian? 


Care you sure you want to quit?’)s 


|, Capitalize the second ‘Y’ 
Fa y y 


(you would have scared 


pscore, 


would be *) 


me 


points. *) 


REPEAT 


turns + 1s 


turns := 
:= cmdlookups; 


lemd 


CASE lemd OF 


take, 

carry: pearrys 
drops: pdrop; 
help: phelps 
invent: pinventorys 
SCOre, 

tally: lscores; 
looks plooks 
shouts pshouts 
open: popens 
unlocks popens 
waddas: pwaddas 
quits Lquits 
nocmd:s: . 


END { CASE lcemd OF 33 


ckproblems: 
{see if any problems were solved by the 
€{ command issued by the player. 


het bs 


UNTIL. lemd = nocmds; 


END ¢€ PROCEDURE listen 3}; 


A ae as ea te pa a re eh ce Se a ery ar em 
{ d Ci) c Oo m m a n d 3 
{ } 
{ call listen in a loop. when the length of 3 
{ head is greater than zero, the user has given} 
{ a travel command so return the first letter } 
{ of *head* to the caller. } 


ca 
1 
i 
| 
i 
| 
i 
I 
i 
! 
i 
i 
i 
! 
i 
i 
i 
i 
i 
! 
! 
| 
1 
! 
I 
i 
i 
! 
i 
i 
1 
| 
| 
i 
! 
| 
i 
I 
{ 
i 
i 
i 
i 
i 
i 
{ 
Ld 


FUNCTION docommand : CHAR; 


BEGIN 


215 


head re Tg 
tail ga FF 
chgloc := false; 
REFEAT 

listens 


UNTIL length (head) = O; 
docommand := headlid; 


END € FUNCTION docommand 33 


Fe an BO oe SO a i 


i 
i 
j 
H 
i 
i 
i 
H 
i 
i 
i 
i 
{ 
i 
i 
i 
i 
H 


w a 


FUNCTION whichway : directions; 


VAR 
ch: CHARS: 
ww: directions: 


BEGIN 
REPEAT 
ch := docommands; 
whichway i= x4 
CASE ch OF 
*n’s IF (head = 
THEN 
whichway 
si lb IF (head = 
THEN 
whichway 
Ze": IF (head = 
THEN 
whichway 
Pw" s IF (head = 
THEN 


216 


*n*) OR 


r= Ms 


*s*) OR 


r= S5 


*w’) OR 


(head 


(head 


(head 


(head 


determine which way the player wishes to go. 


= 


Be Rt RY Le 


ue 


*north’® ) 


*south’ ) 


*"east”) 


*west”*) 


whichway '= ws 


urs IF (head = "u’?) OR (head = *up*) 
THEN 
whichway := us 
"d's IF (head = “*“d*) OR (head = *“down*) 
THEN 


whichway := d3 
rary { empty for now 33 
END { CASE ch OF 33 
UNTIL ch IN dchars; 
writelns; 


END € FUNCTION whichway +3 


{ casas sone sev snste sneen ess evese snmp cosee seen onsne canes seeve mse costs cues ettoe comes soceg seme seats ouom sbmne tues meer cosee aneee suse eumnt ceeee tress stems seman ean sete enme aenes sanen seene mes sees stems se 


¢ n o w a y 


{ secs canen esane seus cones seen esos tenes asses some puree costs sort sete sete ese estes sewes cages sates tonne sates stone mune wees tose crabs coten sttee cious suse sunse seme tune crest ceuoe cuten apne samen enene tener comes muse sees senee eee 


FROCEDURE noways 
REGIN 


writelns; J Capitalize ‘It’ 
writeln ("it is impossible to go in that direction.’ ); 


ch@loc := false; 


END ¢€ PROCEDURE noway 338 


tee et WS RS RS Re bk ee be ee ke es es 


Ls mee te ete seme ce eens te te ene et ee tn ten tn em me mnt tn a ee en ts 


t ° a Vv e 1 


handle travel ta the next location. the 
possible destinations from the current roaom 
or adventure location are passed to travel 
parameters. the value “nowhere’ means that 
there is no way to go in the corresponding 
direction. there is special case code for 
direction “d* oar “dawn’. the location 
*cellar’ has two possible destinations in 
that direction, one of which is the winning 
location. 


ears ee ee es ee es ee of es 8 


217 


FROCEDURE travel; 


FROCEDURE newloc (loc:descs); 
BEGIN 


IF loc = nowhere 
THEN 
noway 
ELSE 
REGIN 


= 


lacation =: loc; 
chgloc ee trues 
END ¢{ IF loc = nowhere }+3 


END ¢{ FROCEDURE newloc 33 


FROCEDURE wingames 
alla J Capitalize ‘You’ 


show (fountain) s 

writeln ("you scored a total of *, pscore); 

writein (* points out of a possible 350.")35 

exit (FROGRAM) ; 350 isn’t actually the highest possible score, 


but | don’t know what the correct score is. 
END ¢ FROCEDURE wingame 33 


BEGIN CKKKKK to or oa vee J] *kKKK} 


IF (lacation IN trollacs) AND trlives 
THEN 
trolactians 


CASE whichway OF 


ns newloc (nlac); 

Ss newloc (sloc)s 

e: newloc (eloc); 

wr newloac (wloc); 

us newloc (uloc)s; 

d: BEGIN 
IF location = cellar 
THEN 
BREGIN 


218 


IF saidwadda 
THEN 
wingame 
ELSE 
IF isopen 
THEN 
newloc (cave) 
ELSE 
noway 
{ END IF isopen} 
€ END IF saidwadda 3} 
END 
ELSE 
newloc (dloc) 
{ END IF location = cellar 3; 
END { case ds: }3 
M REGIN ne Capitalize where indicated by arrows 


wO3 
writeln (7i do not understand that.’); 


writeln (*please try another command’); 


END; t 
END ¢ CASE whichway OF 33 


END < PROCEDURE travel 3; 


i 
i 
f 
! 
i 
1 
i 
i 
! 
i 
H 
i 
i 
! 
i 
I 
j 
i 


D 
n 
cr) 
os 
5 
< 


implement the carry command. call the 

function “abjlookup* to determine which 

object, (if any) has been requested. then 
call “ckqoadies* to see if that object is 
present in the current Location. if so, the 
object is added ta the set “stash* and also 
removed fram the set “*“whatsherelLlocationI’. 


Che he he Se ae he al he Dl 


i 


PROCEDURE pearirys 
VAR 

it: goodies; 
BEGIN 


it #= objlookups 


IF NOT ckgoodies (it) 
THEN 


hes et 3 


he Be he be be we 


Rt ket 


219 


BEGIN J Capitalize where indicated by arrows 


write ("i don’*t see any “*)s 
write (tail)s 
writeln (° here.*)s 


END 
ELSE 
REGIN 


writeln (’ok*)s 
stash := stash + Citds 
whatshereLlLlocation] := 
whatsherelLlocation] -— Citds 
IF (location = wmm) AND (it IN wmmfakes) 
THEN 
wamcount #= wmmcount —- 1 
€ END IF }, 
END ¢ IF NOT it IN ... F8 


END ¢ PROCEDURE pearry 33 


nd 


es 


Bt eet he he 


implement the drop command. similar in 
actian ta the carry cammand (q.v.). 


ro es tk 


he 


PROCEDURE pdraps 
VAR 

it: goodies; 
BEGIN 


it = objlookups 

IF NOT (it IN stash) 
THEN 

REGIN | 


write ("you are not carrying any *); 
wreiteln (tail); 


220 


END 
ELSE 
BEGIN 
J Capitalize ‘Ok’ 
writeln ("ak"): 

stash := stash ~ Citids 
IF (location = wmm) 


THEN 
REGIN 
IF it IN wmmwants 
THEN 
wmmhas #= wmmhas + CitJ 
ELSE 
whatsherelCwmm] := whatsherelCwmm] + Citd 
{ END IF it IN wmmwants 5 
IF it IN wmmfakes 
THEN 
wmmcount s= wmmcount + 1 
€ END IF it IN wmmfakess 3 
END 
ELSE ; 
whatsherellacation] := 
whatsherelLlocation] + Cit] 
{ END IF (location = wmm) 33 
END ¢€ IF NOT it IN stash 3; 
END { PROCEDURE pdrop 33 
{ caine ‘ints cia Geen osven Sven cuss soe basen S600 oncte Stun saves wanes cates come exten bese Gens euesd sutou'conee oauee snsbe'ounee soese Goes este Seorysebts covet vores <s000 conte wwere Suueé see seen esGwsinbere senen ree? eines stone exes wants } 


provided the keys are being carried. 


Cie oe che ae Se 


FROCEDURE popens 
BEGIN 


IF NOT solved (canopen) 
THEN 


handle the open command. the player can 
apen the hatch to the cave under the cellar 


221 


Capitalize where indicated by arrows 


writeln (you can**t open anything here!’”) 
ELSE 
REGIN i 


writeln (*ok.")s 
isopen := trues 
writeln (’one of the hatches is now open. *); 


END ¢ IF NOT solved ... 33 


END ¢ PROCEDURE popen 33 


p w a d d a 


handle the wadda command. in order to win, 
the player must say *“wadda* at location 
cellar. this can only happen after the wmm 
problem has been solved. 


eee mM et 


Bo by BS We be be bs 


FROCEDURE pwaddas 
BEGIN 


IF NOT solved (ftofyouth) 

THEN 

writeln (?i don’*’*t understand baby talk.*) 
ELSE 

BEGIN 


saidwadda := true; 
writeln (* you are clase to the secret.”)3 


t 


END { IF NOT solved ... F8 


END { PROCEDURE pwadda }3 


Shouting doesn’t affect the adventure at all, 


PRBEEOERE iPeEeuys but you could add your own routine if you like 


VAR 
is INTEGER; 
je INTEGER; 


BEGIN | 
writeln (’ok.")3 
FOR i s= 1 TO Soo DO 


Capitalize ‘Ok.’, ‘A’ and ‘There’ 


write ("’a*)s 
FOR j 2s= 1 TO 100 DO 


END; 
writelns 
FOR i = 1 to 15 DO 
BEGIN 
write ("qQ")¢s 
j s= 1 TO 100 DO 


write (7 !")s 
FOR j := 1 TO 100 DO 


writelns 
FOR i s= 1 TO S00 DO 


writeln (? there - now i feel much better.’ )43 


END ¢ FROCEDURE pshout 33 
BEGIN 


dchars := 


C’q’s rn’, "Ss", -e*, ew. Ture 7d’, *y 7 73 
‘q’ is unimplemented; see page 217 
END. ‘x’ is a delimiter, aka “sentinel” 


LISTING 26-4. COMMANDS 2 UNIT 


oo 


223 


Cie sie a ake 


{s+} 
UNIT cmdsis 
INTERFACE 
USES applestuff, 
{#umtl.code} advdata, 
{$ucmds3.code} cmdss; 
PROCEDURE phelps 
IMPLEMENTATION 
VAR 
asked: INTEGER; 


FROCEDURE phelps; 
BEGIN 


IF asked = 2 
THEN 


ELSE 


iicalenih J Capitalize ‘Help’ 


writeln ("help is on the way’)s 


asked := asked + 1; 
END; 
END { PROCEDURE phelp 33 
BEGIN 
asked s:= QO; 


END. 


224 


this unit contains the procedure phelp. the 
help command needs no access to data in unit 
“advdata’*. ait does use procedure 
however, and hence USES unit cmds3. 


"show" , 


lod tad 


ht tt ie 


The Adventure 3 description database in Appendix C 

h the! . € contains no “helpspiel” entry. This causes part of the “start” 
alas il sia deine description to be displayed instead. To fix this bug, add your 
own helpspiel as the final entry in the database (see page 303.) 


LISTING 26-5. COMMANDS 3 UNIT 


¢€ Vase Ss ass gee Soot be Sntcn Bison Sans even Serbs pes ees sav Stan sce SG sears die csc scene aose she, Sob tte ioe fee } 
{ source file: cmds2.text 
€ sis wwene eee oahay Sue nee epoca eens ince ua pa Seve Sescs oes pe Sep oan tee rin eed ets “ne op thcan eben eal Abickes } 
{—-- Seep “eat ge ent: gen Sass nabs 'stbes ams v0 saoes anes petites dnien oem soot 900n8 ein esees vine Waren, t ee ests teed Same Goare eerie ene Sand sSbthomsee canted Sones mom Ys 
{ c¢ m ds 3 uom ait 3 
t 3 
{ This unit contains procedures implementing 2 
{ cammands. The particular commands implemented? 
{ herein need access to the unit advdata, but > 
{ not ta the unit “probs’. + 
{ 3 
Lome ie SiGe tai eis evan TD cia py Game SEAN on Tere sc eas scans abe eae pare yes is SOE Si aS gacnn 6A GUS SCE <8 ata mabe eg SESS Si SoS Ga ca ee SERS oo} 
{$+} 
UNIT cmdsiys 
INTERFACE 
USES applestuff, 
{fumti.code} advdata; 
TYFE 
directions = My Se O@aWa lla aX) 8 
cmds = (carry, drop, help, score, invent, 
take, tally, look, shout, open, 
unlock, wadda, quit, nocmd); — 
pname = STRINGC4OIs; 
storyline = STRINGLBOI; 
byte = O..2553 


whichsect (indexsection, descsection) ; 
placerec = 
RECORD 


CASE section: whichsection OF 


225 


indexsection: ( tableentrys: INTEGER) s 


descsection: ( name: pname; 
id: INTEGER; 
dbegin: INTEGER: 
dend: INTEGER; 
links byte); 
ENDs 
VAR 

xfiles: FILE OF placerecs 

narrate: FILE OF starylines 

places: ARRAY Cdescs] OF placerecs: 

cammancds: STRING; 

heads: STRINGS; 

tails STRING; 

cmdnames ARRAYCcmds] OF STRING; 

chaloc: BOOLEAN; 


{ has player maved since last cmd? } 


PROCEDURE pinventorys 

PROCEDURE ploaks 

FROCEDURE show (where: descs) ; 

PROCEDURE showgoaodiess: 

FUNCTION ob jlookup: qoodiess 

FUNCTION ckgoodies (it: goodies) : BOOLEAN; 


IMPLEMENTATION 

VAR 
rs descs; 
Plurals: SET OF goodies: 
SOMeS: SET OF goodies; 
UsSeanss SET OF goodies: 


£ 


{ for benefit of showgoodies } 


t 
‘ 
i 


S h Oo w 


Retrieve descriptions from the database 
and display them on the player’s console. 


ea ce et e's 


ht bt os Rs LS 


226 


The argument to show is of type “descs’ 
which includes descriptions of situations 
and spoken words as well as descriptions 
of lecations. Show uses the ord function 
to detect what kind of description is 
involved. The description of locations 
suppressed if the player has not changed 
locations or if the location has been 
visited recently. If the player has not 
changed location, then nothing happens. 
If the player has visited the same place 
recently, then just the short description 
is displayed. 


Bt bee bt es Re bs RS be be be ke te ne 


AAA A eI Me ee ee cl es 


PROCEDURE show; 


VAR 
i: INTEGER; 
BEGIN 
IF (chgloc) OR 
(ard (where) = ord (nowhere)? 
THEN 
BEGIN 


IF (visitedClacatian] = 1) OR 
((visitedClocation] MOD 4) = ©) OF 
ford (where) = ard. (nowhere) ) 

THEN 

REGIN 


WITH placesEwherel] DQ 
BEGIN 


FOR i := dbegin TO dend ba 
BEGIN 

seek (narrate, i) 3 

get (narrate)s 


write (narrate™); 


END ¢ FOR i s= wen 8 
END ¢ WITH placeslEwherel] 3}; 


END 


227 


ELSE 


BEGIN dso 
ui Capitalize 


write ("you are *)3; 
writeln (placesC€wherel.name) ; 


END ¢ IF visitedClocation] = 1... 


rd 
“as 


END {¢ IF chglec 33 
IF ord (where) “< ord (nowhere) 
THEN 

showgocdiess; 


END {€ PROCEDURE show 33 


Arm 
3) 
a 
ia} 
= 

0 
o 
ra) 
oa 
e 
HH 
fi 
ke bs te 


{ Print a list of the objects present at the} 
{ current adventure game lacatian. } 


me eee ee te me ee ee et ne ee te ee tam et msn esr at Ae at ey tse ee ys nee ne me "H 


PROCEDURE showgoodies; 


VAR 
labj: goodies; 
BEGIN 


FOR lobj = nuggets TO noabj DO 
IF labj IN whatsherellocation) 


THEN 
REGIN 
IF lobj IN plurals 
THEN 
write ("There are same *) 
ELSE 
IF lobj IN somes 
THEN 
write ("There is some *) 
ELSE 
IF lobj IN useans 
THEN 
write ("There is an *) 
ELSE 


228 


write ("There is a “*) 
{ END IF lobj IN useans } 
< END IF lobj IN somes } 
{ END IF lobj IN plurals 33 


write (abjnamellobjJ); 
writeln (* here.”)s; 


END {¢< IF lobj IN whatshere... 3 
{ END FOR lobj s= wae 43 


END ¢ PROCEDURE showgoodies 33 


mmr mee ee nee ee te reste see tn ans tn ate me ee an ms ste see ue see te ae ae aan smn me te ee ar mae en ete ein eens evens canen enane oats -} 
{ Q b j 1 Q a k ul p } 
if + 
{ Determine if the name typed by the player ?} 
{ in a carry or drop command is the name of } 
{ any object actually part of the game. If 3 
{ so, return the internal value of the } 
{ object in question. 3 
et cee ee es mee ene ese ee toes ees ese ees a tn me tee i wens meee wns mms me tee a es sem seme mn mn sae seme 
FUNCTION objlookups 
VAR 

lobj: goodies; 
BEGIN 

ob jnameCnoobj] s= tails; 

lobj := nuggets; 

WHILE tail ¢2> objnameClobjJ] DO 

lobj s= suce (lobj); 

objlookup := lobj; 
END {€ FUNCTION objlookup 33 
Lone 05 Sena oasin res etebe wets senon nes ese sctba sanio soins tule Speen sovee Ses osee bueah Sines wevesicoese sous less Se0es coten shane upon on sn em ne ene eens ete seme tne att es see ee ne 
{ Cc k q c Q d i e $8 } 
{ 3 
{ See if an object is present. 3 


FUNCTION ckgoodiess: 


229 


BEGIN 


IF it IN whatsherellocatian]) 


THEN 

ckgoodies :F true 
ELSE 

ckgoodies := false 
{ END IF it IN waa 335 


END ¢ FUNCTION ckobject 33 


p 1 Q QO 


Cie SE ote he 
: 


rae es 


PROCEDURE plook; 


VAR 
savchgs BOOLEAN; 
savisits INTEGER; 
BEGIN 


gsavchq = chgloac; 
chgloc := true; 


Implement the lock command. This forces 
out the full description of the current 
location. The variables chgloc and 
visitedClocation] must be temporarily 
reset in order to accomplish this goal. 


Savisit := visitedClocationd; 


visitedClocation] := 13 


show (location) s 


visitedClocation] := savisit; 
chgloc := savchgs 


END { PROCEDURE plook 1}; 


{ nmos sates ssove soone senee sonrs frome sneee temp sume cose ene tues seams sense sesee Some oneer steno teens seete sence sents Sener sunue arene snes mene neues senet sates seems tents sewuh ae seu oente tutte Heute anaes eaves seine Hines 
{ p i n v e n 

t 

<{ Implement the inventory command. Print 


{ the names of all objects carried by the 


{ player. 


{ Ah eoabb es ete seat cee ol sain Sinn nS aes dn sess ress bins opts tm nies iso’ 


230 


Ret at RS 


St bs bet es Re 


Rt best he ks be Oy 


ot 


FROCEDURE pinventoary; 
VAR 

lobij:s goodies: 
BEGIN 


IF stash «3 (7 

THEN 

BEGIN 
writeln ("You currently halds *); 
FOR lobj s= nuggets TO noobj DO 
BEGIN 


IF lebj IN stash 
THEN 
writeln (cbjnamellobjd) 
{ END IF 35, 
END < FOR lobj := } 
END ¢ IF stash <> C1] 43 


END ¢ FROCEDURE pinventory 33 
BEGIN 


plurals := Cnuggets, diamonds, carvings, 

wineskins, flowers, keys, 

antlers, talons, feathers, 

eggs, cymbals, bones, berriesd; 
somes := Cwine, amethyst, flint, 

incense, silver, leather]; €—— add beryl’ to this list 
useans := CLaxe]; €—— add burner’ to this list 


cmdnameCcarry f= "carry’ 5 
cmdnameCdrop J r= "drop"; 
cmdnamelChelpd 2:= “help’; 
cmdnamelinvent J := “inventory’s; 
cmdnameCtally] = "tally"; 
cmdnameCtaked = “take”; 
cmdnamelCscored = *score’s 
cmdnamellookd = "“Look’s; 
cmdnamelC shout J r= *shout’; 
cmdnamelopend r= open’; 
cmdnamelLunlock] r= Funleck’*; 
cmdnameCwaddal r= ?wadda’s 
cmdnameCquitd = * quit’; 
cemdnameCnocmdd 2= “sentinel’; 


231 


reset (xfile, “mtadv.x*) 
reset (narrate, “mtadv’) 


‘ze ae 


“mtadv.x” and “mtadv” are the database files used 
by Adventure 3 for descriptions. 


ror= start; Their location on disk is hard-coded into the game, and 
seek (xfile, 31)3 Pascal will only look there. As written, Pascal expects 
get (xfiled; these files to be on the disk in Drive 1. See “A note 
placesEri] := xfile*; about disk Drive numbers in file names and commands” 


on page 3 of this PDF for more information. 
REPEAT 


roe suce (rds 
qet (xfile)s; 
places(r] := xfile: 
UNTIL r = helpspiels 
close (xfile)s; 
chgloc := trues 
{ Force aut description of start } 


END. 


LISTING 26-6. PROBLEMS UNIT 


i 
t 
i 
i 
i 
t 
i 
Hy 


this unit cantains the single function: 
solved. solved is passed an argument of type 
"problems’. the argument corresponds to one 
of the boolean expressions used to detect the 
solution of problems and components of 
problems. the rest of the adventure code 
uses code like: 


i he ah te a SE ake eS al 
Be RS RS ee ke ee 


IF NOT selved (luretrolls) 
THEN 


instead of using the complex expressions. 
the details of the problem expressions are 
contained here and may be changed without 
forcing the rest of the adventure game code 
change as well. 


Che a he Me on Me le he Se Sl ee a 
Se 


UNIT probss 
INTERFACE 


USES applestuff, 
{tumtil.coade} advdata;: 


TYPE 


problems = (wmmhappy, luretrolls, 
bonetrolls, canopen, ftofyouth) ; 


FUNCTION solved (which: problems) : BOOLEAN: 
IMFLEMENTATION 


FUNCTION solved; 
BEGIN 


solved := false; 
CASE which OF 


whmhappy : solved := 
(wmmhas = wmmwants) AND 


(whatsherelCwmm] * wmmfakes = £1); 


luretrolls: solved := 


(troltime * © ) AND 
CCturns ~- troltime) = ©) AND 
C(turns - troltime) < 25); 


banetrolls: solved := 
bones IN whatsherel(mtcavelal; 


canopens: solved := 
(location = cellar) AND 
(keys IN stash) s 


ftofyouth: solved := 
(wamhas = wmmwants) AND 
(whatshereCwmm] * wmmfakes = (£3) 
(lacation = cellar); 


AND 


233 


END ¢ CASE which OF } 


END ¢€ FUNCTION solved > 


BEGIN 
END. 


LISTING 26-7. ADVENTURE 3 DATA 


Seana soos senha Sine iat eet asestachs'snean ins nbeah acne’ ste nb Sosis Sebo obi Soe ede se gees boson cveve' doen vies We 

. 2 

{ source files mtl.text 3 

Gy secs sabss toca te er we tet Sg sae avon Sebel arb! sak eel sb Sse oS00sSoeh tice hint oaaed SHOE HET: PO 

£ 3 

¢ scott esos svgnninab Stile, nes Soesb Have pa’ sess deep icieod Asbo iv es ibs bavba noe, bet slanb tps taot asa. an ished ducae anh dsn a¢ec codes pens desi etn been dnt ceTnobge weben babs enbns ie (esessisvand one } 
£ } 
{ ro OF mM 8 “wom ait 3 
£ he 
. 7 
{ types and variables used by all other units } 
{ in mountain adventure. this includes the 7} 
{ descs enumerated type, extended to account 3 
{ for some descriptions that are not actual 3 
{ game locations. the goodies type accounts } 
{ for all objects in the adventure and the } 
{ objname array contains names for all of 3 
{ them (for use in descriptions and in 3 
t commands). > 
t 3 
E airosegy eo reassess 60m Sitah Ses CSG es a aisb a Ui aS aba cepa Scones inet ak Sabon eS deat aca ats gs abet soe iweb Heese Saw sande est spitardeessi dea 
tL a 
($e+3 


UNIT advdatay; 
INTERFACE 
USES applestuffs; 
TYPE 


descs = (start, traili, trail2, trail3, trail4, 
trailS, trailé, coll, col2, col3, col4, 
rubblel, rubble2, mound, monastery, 
cellar, cave, slopel, slope2, slopes, 
slope4, slopeS, ruti, rut2, ruts, 
steepl, steep2, steep, steep4, steeps, 
peaki, peak2, peakl, peak4, peaks, 


234 


saddlel, 
mtcavelb, 
mtcave2c, 
trolls, 
nowhere. 
wmmbl ab « 
WMMS PS x 
tgablure, 
trfollow, 


goodies = 


fakes 
trash 
wmamgoady 
collection 


VAR 


lacatioans 
trollocs: 


wmmwant ss 
wonmhass 
womf AKeSs & 
stash: 


whatshere: 
visited: 
ob jname: 


tLurnss 
troltime: 
wmmcount s 
trlives: 


saidwaddas 


walkstick, 


saddle2, saddle, 
mtcavelc, mtcave2Za, mtcaveczb, 
gullyl, gully, hilll, 
ridgel, ridge2, wmm, chasm, 
cellardown, fountain, trtalk, 
womharp, wmmhello, wmmspl, 
woamsp3, wmmsp4, wmmsps, 
tgabbones, trhungrys, 
helpspiel); 


mtcavela, 


“helpspiel” doesn’t exist in the description database 
on page 303, see pages 224 and 303. 


start. .nowheres 


(nuggets, Silver, diamonds, beryl, 
amethyst, carvings, wine, wineskins, 
flowers, axe, hammer, flint, keys, 
antlers, talons, feathers, eggs, 


leather, cymbals, drum, 


bones, scroll, wheel, ramshoarn, 
berries, knife, burner, incense, 
tooth, noaabj)s 


Hi 


i 


nuggets..wineskings 
flowers. .bones; 
scroll..tooth; 
SET OF goodies: 


rooms: 
SET OF rooms; 


collections; 
collections; 
collectian; 
collectian; 


ARRAYE roams] OF callections; 
ARRAYCrooms] OF INTEGER: 
ARRAY Cgoadies] OF STRINGLISI; 


INTEGERS 
INTEGER s 
INTEGERS: 
BOOLEAN: 
{true when trolls still around? 
BOOLEAN: 


235 


isopen: BOOLEAN: 


eaten: BOOLEAN: 
killeds BOOLEAN: 
dane: BOOLEAN; 


FUNCTION rand (low, high: INTEGER) : INTEGER; 


IMPLEMENTATION 
VAR 


re rooms: 
{ loop control for initialization } 


FUNCTION rands 
VAR 

mx. Cy Gs INTEGER; 
BEGIN 

rand := O; 

IF low = high 

THEN 

rand == low 


= high ~ low + 1s 
= (maxint ~ high + low) DIV c + 13 
= mx *& (high ~- low) + (mx - 1); 


REFEAT 
d := randam 
UNTIL d «= mxy 
rand := low + d MOD cy}; 


END € IF low = high 33 
END { FUNCTION rand 33 


FROCEDURE initds; 
BEGIN 


location := starts 


ob jnamelCnuggets] r= "nuggets" s 
ob jnamelsilver] = “silver’s 


236 


"diamonds"; 
*beryl* 5 
"amethyst’ 3 
*"carvings’ j 
"wine"; 
*7wineskins’ 4 
*flowers’ 4 


ob jnamelCdiamonds] 
ob jnameCberyl J 

ob jnamelamethyst ] 

ob jname[carvings4 

ob jnamelCwined 

ob jnamelCwineskins4 
ob jnamel flowers) 


Hou HOW OH ROD OH 


ob jnamelLaxed 7 axes 

ob jnameChammer J "hammer ” ; 
ob jnamelflintd Flint’; 
ob jnamelkeysd "keys": 


Zantlers’ ; 
*taloans’: 
*"feathers’ 3 
"eagle-eqgs’ ; 
*walking-stick’ 5 
*leather’; 
*cymbals’” ; 

> drum’ s 

*bones’* 5 
*scroll’s 
*prayer-wheel *; 
7 ramshorn’ § 
*berries’ ; 
*knife’s 
*incense-burner’ ; 
*incense’; 
*tooth* s 


ob jnamelantlersd 
ob jnameCtalons) 
ob jnamelfeathers) 
ob jnameleggs4 

ob jnamelCwalkstick] 
ob jnamelCleather J 
ob jnamelCcymbals] 
ob jnamelCdrumd 

ob jnameCbonesd 

ob jnameCscroll 3) 
ob jnamelwheel J 

ob jnameCramshornd 
ob jnamelCberries] 
ob jnamelCknifed 

ob jnameCburner I 
ob jnamelincensel 
ob jnamelCtooth) 


Huu ft t tf t bo ot ft bh hho oR 


FOR r s= start TO chasm DO 
BEGIN 


whatsherelCr] := 
visited(€rd r= 


END ¢€ FOR r s= start ««. 33 


END { FROCEDURE initi 33 

ae . “diamonds” are never assigned to a location here. The game works fine 
PROCEDURE inites without them, but you could assign them to any location on the map, or 
BEGIN even better, write a routine which places them in a random location. 


whatsherel(peaki J scroll ds 


2= OE 
whatshereCpeak2] := Cramshornd; 
whatsherelpeaks] := CLincensel; 


237 


whatsherelpeak4] 
whatsherelpeakSd 


ow tou 


Cburner ds 
Cwheel J; 


whatshereltrail 3] : Cwalkstick, axel; 
whatsherelcave] : Cbhonesd; 
whatsherelCmound] := Cflowersds; 
whatsherel(mtcavelbd := Cknifed; 
whatsherelCruts] r= Cnuggetsd; 
whatsherelrubble2d := Csilverd; 
whatshereltrolls] := Lamethyst, 
WINE. 
wineskind; 
whatshereCgullyld := Cheryl; 
whatsherelridge2 := Ccarvings, 
keySs 
berriesd; 
whatsherel(mtcavezal r= Cflintd; 
whatshereltrailé] := CLantlersd; 
whatsherel(steepS] 2:= ECtalonsd; 
whatsherelsaddle2d := Cleatherd; 
whatsherelsteep4] r= CLegqgs 1s 
whatsherelCmonastery] := Cdrum, cymbalsi3 
whatsherelcellard] := Chammer; 
whatsherelsteep3] := Cfeathersd; 
trollocs := (Ctrolls, slope2, steep2, peak=, 
saddlel, coli, saddle2, 
mtcavela, mtcavelb, traild, 
trail2, rutl, ridge2d; 
wmmwants := Cscrall..toothd; 
woamhas := C5 
womfakes := Cnuggets..wineskinsd; 
stash s= C3 
turns 2 Of 
troltime := O; 
wmmcount = OF 
trlives r= trues 
saidwadda := false; 
isopen := false; 
eaten := false; 
dane := false; 
END { FROCEDURE init2 33 
REG IN 
initl,; 
initis 
END. 


238 


Larger Programs: Using UCSD Units 


As your adventure game programs grow larger and 
larger, it will become more and more difficult to 
work with them. Even with the include option of the 
Pascal compiler, the program itself is still one 
large, monolithic piece of code. Using lots of proce- 
dures and functions helps to organize it, but it would 
be convenient to break it into pieces. The UCSD 
Pascal extension known as a unit is a way to break 
large programs into smaller ones. In this chapter I 
shall discuss UCSD units and show how to use them 
in adventure games. 


WHY UNITS? 

You may question the usefulness of units. As 
with other techniques, there are both advantages 
and disadvantages to using units in writing Pascal 
programs. I will emphasize the positive and discuss 
the advantages. 


m@ Each UCSD Pascal unit is separately compiled. 
This can mean shorter compilation times if you 
have enough units: instead of having to recompile 


the entire program, you only have to recompile 
one unit. This is not the whole story, as you shall 
see later on. In general, however, units, when 
properly used, can save you time. 

m@ Each unit is a self-contained piece of the larger 
program. You can treat it like a small program 
itself. This reduces the amount of code you have 
to deal with at a single time and simplifies your 
design and programming effort. It is possible to 
misuse units and throw away this advantage. 
However, if you keep logically related code to- 
gether in a single unit or in several related units, 
you will simplify your programs. 

@ Units can be used to “hide” certain information 
from the programs that use the units. This is a 
subtle concept that takes awhile to absorb. I shall 
try to elucidate further as I elaborate on units and 
their use. 


THE UNITS IN ADVENTURE 3 


The following pages describe the units used in 
Adventure 3 and how they interact with each other. 


239 


The Main Program 


Every Pascal program must have a main pro- 
gram, whether it uses units or not. The main pro- 
gram is not itself a unit, but it must contain a uses 
statement that requests all the units that the pro- 
gram requires. I explain the uses statement when I 
go into the details of unit syntax and semantics. 
There are seven units used in Adventure 3, one of 
which comes from the SYSTEM.LIBRARY. The 
remaining six units are part of the code of Adven- 
ture 3 itself. 


The Locations Unit 


Units are arranged in a hierarchical fashion, as 
noted in the diagram in Chapter 25. The top level 
units contain uses statements for the lower level 
units that they rely upon. The locations unit is the 
highest level unit in the Adventure 3 hierarchy. It 
contains the locations procedures pwmm and 
pcellar. Any other location procedures that might 
be needed in further developing Adventure 3 should 
be placed into the locations unit. 


The cmds1 Unit 


The cmds1 unit contains the general support 
procedures for command processing: cmdlookup, 
listen, docommand, whichway, noway, and 
travel. In addition, it contains three of the specific 
command procedures: pcarry, pdrop, and pshout. 
Finally, it contains the code for dealing with the 
problem surrounding the trolls: ckproblems, 
trolmeal and trolaction. 

If a procedure or function needs to call the 
solved function, which is in the probs unit, it must 
be included in cmds1. Otherwise, it may be placed 
in one of the other cmds units. 


The cmds2 Unit 


This is a very small unit—it contains only the 
phelp procedure. The procedures that go into the 
cmds2 unit are those specific command procedures 
that do not need access to any data declared in the 
advdata unit. Because most commands do need ac- 
cess to some sort of data, very few procedures will 
wind up here. Commands that could be added, 


240 


whose implementation procedures could be placed 
in cmds2, are those that cause no change to the 
status of the adventure; instead they cause some 
sort of reaction from the guide in the form of 
dialogue. The help command is a good example. 
The only side effect of the use of the help command 
is an increase in the count of how many times help 
has been requested. This total is used in the scoring 
at the end of the game. 


The cmds3 Unit 

The cmds3 unit contains two other command 
implementation procedures; pinventory, and 
plook. It also contains procedures used in display- 
ing descriptions of locations and objects: show, 
showgoodies, objlookup, and ckgoodies. None of 
the functions or procedures in cmds3 need access to 
the probs unit. That is the general rule for putting 
them in cmds3 as opposed to cmds1. They do need 
access to various pieces of adventure game data, 
however, and that is the criterion used to decide 
that a procedure should go into cmds3 instead of 
cmds2. 


The probs Unit 


I use the abbreviation for the probs unit, be- 
cause the full name, problems, is used inside the 
unit for the name of an enumerated type. Probs 
contains a single function called solved, which 
evaluates whether various problems in the adven- 
ture have been solved or not. It is not the sole 
arbiter of problem solution. However, it is a conve- 
nient way to centralize and organize the approach to 
parts of problems that depend on complicated 
Boolean expressions. 


The advdata Unit 


This unit contains the bulk of the types and 
variables needed in more than one place elsewhere 
in the adventure. It is at the bottom of the uses 
hierarchy so that all those units above it can “see” 
the data that it declares. 


UNITS: SYNTAX AND SEMANTICS 


A unit is a self-contained UCSD Pascal compi- 
lation entity. That rather pompous statement sim- 


ply means that you can compile a unit by itself. 
Several units can be compiled separately as part ofa 
single larger program. When you compile a unit, a 
code file is produced, just as when you compile a 
program. The difference is that the resulting code 
file may not be X(ecuted. It must be linked with 
other code files, including at least one from a pro- 
gram that uses the unit. There is a relationship 
between the code files for different units of a single 
program. It mirrors the relationship dictated by the 
uses statements in the various units and program 
itself. I discuss these relationships further when I 
delve into uses in detail below. 

Every unit has three parts: a unit heading, an 
interface part, and an implementation part. The 
interface part declares what in the unit is public, 
that is, available to those units and programs that 
use the unit. The implementation part implements 
the functions and procedures declared in the inter- 
face part and may contain other Pascal code whose 
scope is limited strictly to the implementation part. 


The Unit Heading 


The unit heading is analogous to the program 
heading. It appears at the start of the unit and gives 
a name to the unit. The syntax is trivial: 


UNIT unitname; 


The only other requirement for units is that the 
compiler swapping option must be turned on before 
the unit heading: 


{$S+} 


The Interface Part of a Unit 

The interface part of a unit is initiated by the 
key word INTERFACE. The interface part there- 
fore may contain CONST, TYPE, and VAR decla- 
rations as does a normal Pascal program. All con- 
stants, types, and variables declared in the inter- 
face part behave as if they were declared in the main 
program’s outermost part. This is another way of 
saying that these declarations are public. People 
also describe the situation by saying that the inter- 
face declarations are exported to the rest of the 


program (actually to any program or unit that uses 
the unit in question). 

After the CONST, TYPE, and VAR parts of the 
interface, there may be declarations of procedures 
and functions. That is, the procedures and functions 
whose code will appear in the implementation part 
have their headings in the interface part. The idea 
here is that the interface part tells you what proce- 
dures and functions are available in the unit and how 
to call them. It does not reveal any details of how 
the procedures and functions are actually im- 
plemented. This allows the implementation to 
change without requiring the users to recompile 
their own code. This may seem trivial on first 
glance, but it is actually one of the important ideas 
in modern programming language design. 

The interface part of the unit ends with the last 
procedure or function declaration. The interface 
may contain no code. It must declare at least one 
procedure or function; that is, you cannot have a 
unit that contains only data. 


The Implementation Part of a Unit 


The implementation part of a unit is started 
with the key word IMPLEMENTATION. It is the 
private part of the unit and may be changed without 
requiring the users of the unit to recompile their 
code. This statement is only true if no change is 
made to the interface part. The following things that 
cannot change are 


@ The specific constants, types, and variables de- 
clared cannot change. Any additions or subtrac- 
tions here will cause a need for recompilation. 

@ The headings of functions and procedures cannot 
change: the number and types of parameters and 
the return types of functions must be untouched. 


The implementation part of a unit may have its 
own CONST, TYPE, and VAR declarations. Any- 
thing declared there is strictly private to the unit 
and cannot be referred to anywhere else in any 
program that uses the unit. The implementation 
part must contain code for all the functions and 
procedures declared in the interface part. It may 
also contain its own private procedures and func- 


241 


tions (which may be called from within the public 
procedures and functions inside the unit, but may 
not be called from outside the unit). The code for 
the implementation of the interface procedures and 
functions must not repeat the entire heading of the 
procedure or function. Only the key word PROCE- 
DURE or the key word FUNCTION must appear, 
followed by the name used in the interface declara- 
tion. For example, in the probs unit of Adventure 3 
you find the declaration 


FUNCTION solved (which: problems) : 
BOOLEAN; 


In the implementation part of probs, the code for 
solved begins with 


FUNCTION solved; 


Any repetition of the heading other than the parts 
indicated will cause a syntax error to be generated 
by the compiler. 


The Uses Statements 


Units are used by other units and by Pascal 
programs. The UCSD Pascal language provides the 
uses statement for telling the compiler which units 
are being used. Uses statements immediately fol- 
low the program heading in Pascal main programs. 
Units may also use other units and the uses state- 
ments in units immediately follows the INTER- 
FACE key word. 

Each unit that is used in a program or other unit 
must be located by the compiler in a library code 
file. I shall not go into all the details of li- 
braries in UCSD Pascal. The compiler is nice 
enough to provide a way to avoid the hassle of 
having to install units in libraries before using 
them. The compiler option U may be used to specify 
a code file to search for a unit at compile time. This 
is best illustrated with examples. In the main pro- 
gram of Adventure 3 you see 


PROGRAM mtadvent; 


{$S+} 
USES applestuff, 


242 


advdata, 
probs, 
cmds3, 
cmds2, 
cmds1, 
locs; 


$Umt1 .code} 
$Uprobs.code} 
$Ucmds3.code 
$Ucmds2.code 
$Ucmds1.code 
$uloc.code} 


The first item to note is that the uses statement 
allows a list of units to be specified. The total 
number of units allowed is limited by the number of 
Pascal segments available. This may vary from 
system to system. I never exceed it, so I say no 
more. You should be aware that there is a limit. 

In the above example, each unit except the 
first has a {$U . .. compiler option preceding the 
name ofthe unit. If no {$U ... appears, the compiler 
will search SYSTEM.LIBRARY for the unit. In 
Apple Pascal, SYSTEM.LIBRARY happens to 
contain a unit called applestuff. This unit provides 
the random function used in the advdata unit of 
Adventure 3. If you enter Adventure 3 in your 
system, you should replace the reference to 
applestuff to the equivalent unit (or provide ran- 
dom in some other way). 

When a unit in the uses statement list is pre- 
ceded by a {$U . .. option, the file name specified 
there is used by the compiler. This file must be a 
code file and must contain the code generated by the 
Pascal compiler when that unit was compiled. For 
example, mtl.code must contain the code for the 
advdata unit. If no such file exists, or if the file does 
not contain code, the compiler will issue an appro- 
priate error message and will no doubt have great 
difficulty continuing. 

You can now see that if a unit is to be compiled, 
all the units that it uses must be compiled first. This 
means that the uses statements in all the units of a 
program cannot contain any circularities. For 
example if Unit A said USES B, Unit B said USES 
C, and Unit C said USES A, you could never com- 
pile A! Why? Because in order to compile A you 
would first have to compile B because A uses B. In 
order to compile B, you would first have to compile 
C because B uses C. But in order to compile C, you 
would first have to compile A, because C uses A! 
Therefore, in order to compile A, you would first 


have to compile A—but that is impossible: you can’t 
compile A before you compile A. 

In Adventure 3, mtl.text must be compiled 
first to provide a code file containing the advdata 
unit. Then cmds3 and probs can be compiled in any 
order (they both use advdata). cmds2 can be com- 
piled at any time because it does not use any other 
unit, but it must be compiled before cmds1, which 
uses it. cmdsl may be compiled after advdata, 
probs, and cmds2 are available. locs is the last unit 
of Adventure 3 to be compiled: it uses all the 
others. Finally, the program of Adventure 3 cannot 
be compiled until all the units of Adventure 3 (in- 
cluding locs) have been compiled. 

Compile in this order, followed by mtadvent 
LINKING PROGRAMS THAT HAVE UNITS 


When you compile the main program of Ad- 
venture 3, the resulting code file may not be 
X(ecuted. If you try, the system will tell you: 


Must L(ink first 


The process of linking combines the separately 
compiled main program code file with all the code 
files for the units that it uses. The result is another 
code file, which is X(ecutable. In the UCSD sys- 
tem, linking is accomplished by using the L(ink 
command. This requires that the SYSTEM 
.LINKER program be on line. 

Using the UCSD linker is relatively simple. It 
will engage you in a dialogue. It wants to know the 
names ofall the files it needs in order to perform the 
linking process. It starts out by requesting 
Messages are more user-friendly in Pascal 1.3 

HOST FILE? 


This file will always be the code file of the main 
program for our adventure games. Thus, respond 
with Specify drive volume or disk name if needed: 
“#5:mtadvent” for Drive 2 or 

HOST FILE? mtadvent “dsknm:mtadvent” 


assuming that the main program has been compiled 
into mtadvent.code. 


The linker continues by asking: 


LIB FILE? 


The response here should be the name of a .code 
file containing one of your units. The question will 
be repeated and you should respond by giving the 
names of all the .code files for the units required by 
the program: 


LIB FILE? mt1 Specify drive volume 

LIB FILE? probs or disk name if needed: 
LIB FILE? cmds3 “#5:mt1” for Drive 2 

LIB FILE? cmds2 or “diskname:mt1” 

LIB FILE? cmds1 

LIB FILE? locs (See note on page 3 of this 
LIB FILE? PDF for more information) 


When you are finished, simply pressing RETURN 
will cause the linker to stop prompting for lib files. 
It then requests: 


MAP FILE? 


It is safe to answer this by again pressing RE- 
TURN. The map file is a file of technical informa- 
tion generated by the linker. You won't need this 
file in the ordinary course of linking adventure 
games. 

At this point in the dialogue, the linker will 
read in all the code for the program being linked and 
finally will ask: 


OUTPUT FILE? 


For Adventure 3, respond with Again, specify 
volume # or 
OUTPUT FILE? advent = diskname 
as needed 


The linked program will be placed in the ad- 
vent.code file. You may then execute the adventure 
game by commanding the system to X(ecute and 
answering with advent when it asks Execute what 
program? 


MAINTAINING A 
PROGRAM WRITTEN WITH UNITS 

One of the advantages of using units is that you 
have less compiling to do than with large, 
monolithic programs. Whenever you change a unit, 


243 


you do not necessarily have to recompile all the 
units in the entire program. The general rules are 
as follows: 


@ If you make a change in a unit that does not 
involve changing the interface part of the unit, all 
you need to do to incorporate the change is 


1. Recompile the unit 
2. Relink the program 


@ If you change the interface to a unit, you must 
recompile all other parts of the program that use 
the unit. In order to know which parts are in- 
volved, it is good to keep a diagram of the uses 
relations between all the units in your program. I 
showed such a diagram for Adventure 3 in 
Chapter 25, Fig. 25-7. 


Starting from the top of the uses diagram, work 
downwards until you find the unit whose interface 
has changed. Then recompile that unit and all the 
units (including the main program) above that unit. 


244 


Then relink the program in order to incorporate the 
change. If you are careful, this rule is not difficult to 
follow. On the other hand, if you don’t keep a good 
uses diagram and are careless about recompiling all 
the units that can “see” a change to an interface, 
your program will start behaving very strangely. 
When you have “weird” bugs that you simply do not 
understand, one good idea is to just recompile ev- 
erything. 

To illustrate the above rule, consider the 
probs unit of Adventure 3. If the interface to probs 
is changed, all units above probs must be recom- 
piled. This consists of the units probs itself, cmds1, 
locs, and the main program. 

A unit is considered to be above another unit if 
there is an arrow or sequence of arrows in the uses 
diagram leading from the other unit to the unit in 
question. For example, if we start at locs, there is 
an arrow pointing directly to probs. Thus, locs is 
above probs. Starting at main, we can reach any 
other unit by a sequence or arrows. Thus, main is 
above all units in the program. In other words, main 
must always be recompiled if the interface to any 
unit changes. 


Problems in Adventure 3 


The problems in Adventure 3 are more complex 
than those in earlier adventures. They pose their 
own special implementation difficulties. In this 
chapter I dwell on the techniques used to imple- 
ment the problems in Adventure 3. 


THE PROBS UNIT 


In the introduction to Adventure 3 and again in 
the last chapter, I touched on the probs unit. The 
probs unit exemplifies a new approach to problems 
in general. The solved function in the probs unit is 
used to centralize the evaluation of complicated 
Boolean expressions that are part of the problem- 
solving process. This makes the representation of 
problems in general both simpler and more sys- 
tematic. 

Solved is a Boolean function, that is, it returns 
either true or false. It takes a single parameter, 
which of type problems. The enumerated type 


problems= (wmmhappy, luretrolls, 
bonetrolls, canopen, ftofyouth); 


is declared in the interface of the probs unit. Its 
values represent the individual Boolean expres- 
sions needed for the problems in Adventure 3. The 
units that use the probs unit can “see” the declara- 
tion of problems and can pass those values to 
solved. 

The solved function consists of a simple case 
statement controlled by the parameter which: 


CASE which OF 
wmmhappy: solved := 


(wmmhas= wmmwants) AND 
- etc. 


solved simplifies the approach to problem rep- 
resentation. Take for example the problems value 
luretrolls and its appearance in solved: 


luretrolls: (troltime > 0) AND 


((turns — troltime) > 0) AND 
((turns — troltime) < 25); 


The complicated expression for luretrolls is 


245 


evaluated once and for all in solved. The rest of the m The first time you see them is the first time you 


code for the adventure can use statements like 


IF solved (luretrolls) 
THEN 


or 


IF NOT solved (luretrolls) 
THEN 


which are simple and easy to understand. The ad- 
venture game writer may think in terms of solved 
rather than in terms of the more complicated ex- 
pressions hidden inside solved. 

Hiding complicated problem expressions in- 
side solved can be a debugging aid as well. Suppose 
there are many different references to a given 
Boolean expression (like the one corresponding to 
luretrolis). Suppose the formulation of the expres- 
sion was incorrect. With solved you only have to fix 
one place in the program. The calls to solved don’t 
need to change. Without solved, you would have to 
fix all the places where the expression is used, 
making sure that the expressions in all those places 
were identical. Once you found all the places and 
edited in the change, you would have to recompile 
all the units or programs involved. With solved you 
only have to recompile the probs unit (assuming 
that the fix did not change the interface to the unit). 
In the course of debugging a long and complicated 
adventure, this simplification can be a great advan- 
tage. 


THE PROBLEMS IN ADVENTURE 3 


This part of the chapter describes the various 
problems that must be solved in order to success- 
fully complete Adventure 3. 


The Trolls 


Adventure 3 presents the trolls, a hairy, hun- 
gry bunch of monsters who will eat you if you are 
not careful. The trolls start out at the trolls location 
(where else?) and there are several facets to their 
behavior in the adventure: 


246 


visit the trolls location. 


1. If you arrive at the trolls location empty 
handed, you will be promptly dispatched. 
In short, the trolls will eat you! 

2. If you are carrying the bones with you 
when you arrive at trolls the first time, the 
trolls will follow you. You may thus lure 
the trolls away from their mountain lair. 
This turns out to be the only way to get 
past their location. 


@ The trolls will follow you (as long as you are 
carrying the bones) to any of the locations in the 
collection trollocs: 


trolls, slope2, steep2, peak2, saddle1, 
col1, saddle2, mtcave1a, mtcave1b, 
trail5, rut1, ridge2 Also trail2, but oddly 
not mtcavelc 
If you drop the bones at any time before solving 
the trolls problem and subsequently visit one of 
these locations, the trolls will materialize there 
and promptly eat you. 

@ The trolls may be pacified by luring them to 
mtcave1a and dropping the bones there. 

w If you do succeed in pacifying the trolls, their troll 
teeth will forever after be located in mtcave1a. 
You can pick them up and carry them elsewhere, 
drop them, return to mtcave1a and there will be 
another tooth there! The trolls break their teeth 
crunching away on the bones you drop in 
mtcave1a and their teeth then litter the floor of 
said cave. 

@ Once the trolls have been pacified, they no longer 
bother you. They are removed from the adven- 
ture forever after. 


Finding the Bones 


The bones are initially located in the cave 
beneath the cellar beneath the monastery. In order 
to get into the cave, a hatch must be opened. This 
requires that you have the keys with you. You must 
find the keys (located elsewhere in the adventure) 
and return to the cellar in order to get into the cave. 


Of course, just finding the bones is not enough. 
You must also carry them with you back to the trolls 
location in order to solve the trolls problem. There 
are various subtle hints to this effect in the descrip- 
tions that accompany being eaten by the trolls when 
you fail to have the bones. After a few failures, you 
will figure out what to do. 


Making the Wise Man of the Mountain Happy 


Strewn about the map of Adventure 3 are many 
seeming treasures. These treasures are fakes as far 
as the wise man of the mountain is concerned. He is 
more interested in “spiritual” things. If you bring 
the fakes to him and drop them at the wmm loca- 
tion, you eventually get reprimanded for your ef- 
forts. The problem of making the wise man of the 
mountain happy involves: 


@ Realizing that the conventional treasures are of 
no interest to the wmm. 

@ Figuring out which items in the adventure will 
make the wmm happy. 

@ Bringing all the wmm treasures to the wmm 
location and dropping them there. 


There is a subtle way to figure out which items 
the wmm values. If you bring something to the 
wmm location that the wmm considers to be of true 
value and drop it there, it disappears completely. In 
other words if you say drop x and then try to carry x, 
you will get “I don’t see any x here” in reply. This 
will seem strange at first, but the idea is that the 
wmm is immediately and silently spiriting those 
items away to his own private collection. 


Opening the Doorway to the Fountain of Youth 


There is a secret word that the wise man of the 
mountain will reveal after you have brought him all 
his true treasures. This word, if uttered in the 
cellar beneath the monastery, has the effect of 
opening the door to the fountain of youth. If you then 
go down you will win the game. 


IMPLEMENTING ADVENTURE 3 PROBLEMS 
I now delve into the Pascal details of Adven- 


ture 3 problems. I shall occasionally use the 
precondition—postcondition terminology of 
Chapter 15. If you skipped that chapter earlier, now 
would be a good time to read it. 


The Trolls: First Encounter 


The first implementation headache involves 
the trolls. The problem is how do you prevent the 
trolls from “following” the player before the first 
encounter with them? If you look at the map of 
Adventure 3, you will see that it is impossible to 
reach the trolls location without first passing 
through at least two or three other locations in the 
trollocs set. The solution to this implementation 
problem is implicit in the use of the variable trol- 
time. 

The variable troltime is an integer. It is ini- 
tialized to 0 in the initialization part of the advdata 
unit. As long as troltime remains equal to 0, the 
trolls can only appear at the trolls location. The 
trolocation procedure, invoked whenever the value 
of location is one of those in trollocs and the trolls 
are still “alive” handles this: 


IF (troltime = 0) AND (location = trolls) 
THEN 


The trolocation procedure is responsible for 
more than just the first encounter with the trolls. 
The other code in trolocation must be written so 
that it will not be invoked before the first encoun- 
ter. The rest of the code consists of two IF state- 
ments. In both of these statements the condition 


solved (luretrolls) 
must be met before the statements will be exe- 
cuted. In turn, the Boolean expression for the lure- 
trolls problem contains the condition 

troltime > 0 
All in all, troltime guards the first encounter and 


forces it to occur at location = trolls. 
What Happens at Trolls the First Time? 


247 


The trolls problem as described earlier requires 
that the player be carrying the bones in order to 
avoid being eaten. This is easy to express in Pascal: 


IF bones IN stash 
THEN 
BEGIN 
troltime := turns; {solve luretrolls 
problem} 
show (trhungry); 
END 
ELSE 
trolmeal 
{END IF bones IN stash}; 


If the player is carrying the bones: 
bones IN stash 


the luretrolls problem is solved (for the time being) 
by setting troltime equal to the value of turns. Of 
course, by now the value of turns must be > 0. Ifthe 
player should be unlucky enough not to have the 
bones, a procedure called trolmeal is invoked. 
Needless to say, the trolls’ meal is not potatoes and 
gravy! 

Here are the preconditions and postconditions 
for this problem: 


Preconditions: 


(troltime = 0) AND (location = trolls) 
bones IN stash 


Postcondition 
troltime > 0 


Of course, if the preconditions are only partially 
met, the trolls eat the player and the game is ended. 


The Trolls: After the First Encounter 
The problem expression for luretrolls has the 
following component: 


(turns - troltime <= 25) 


248 


This means that the player must get rid of the trolls 
within 25 turns after first luring them. This is plenty 
of time, once you know how. It may take a few 
games (and troll meals) to learn how to get rid of the 
trolls. After all, the trolls have to have some fun 


too. t There’s no code for this, and a big bug! See page 212 


Getting Rid of the Trolls. In order to get rid 
of the trolls for good, the player must 


1. Lure the trolls to location mtcave1a. 
2. Drop the bones and scram. 


The trolls will remain in mtcave1a and mer- 
rily feast on the bones left behind. Then they will 
melt away into the mountains, never to appear 
again. The implementation problem this poses is 
how to detect the successful completion of 1 and 2 
above.In particular, how do you detect when the 
player has left the cave after dropping the bones 
there in the presence of the trolls? 

There is a special procedure in Adventure 3, 
called ckproblems. It is silently invoked after 
every command. It is called in order to see if the 
execution of acommand has caused any problems to 
be solved. The current implementation of Adven- 
ture 3 caters to two situations: 


1. The player has successfully lured the trolls to 
mtcave1a and dropped the bones. This situa- 
tion may be detected while the player is still in 
location mtcave1a. 

2. The trolls have been disposed of and the player 
has left mtcave1a. It is at this juncture that the 
tooth is made to materialize in the cave: part of 
the problem solution is to return to the cave and 
find the tooth. This takes courage on the player’s 
part because he knows the trolls were there 
when he last visited the cave. The wmm must be 
given the tooth (among other objects) in order 
for the player to win the game. 


The first situation is detected as follows: 


Precondition 


solved (luretrolls) AND solved (bonetrolls) 


Postcondition 


trlives = FALSE 
NOT (bones IN whatshere[mtcave1a]) 


The second situation is detected as follows: 
Precondition 

(NOT trlives) AND (location <> mtcave1a) 
Postcondition 

tooth IN whatshere[mtcave1 a] 


The first clause of the second situation is only made 
true by successfully achieving the first situation as 
you can see in the postconditions for the first situa- 
tion. 


The Wise Man of the Mountain 


The wise man of the mountain sits on the 
grandest peak in the mountain range and dispenses 
“wisdom.” The player must bring the wmm (ab- 
breviation for wise man of the mountain) the 
goodies that he deems valuable. The wmm goodies 
are not the conventional treasures like gold and 
silver. Part of the challenge to the player is to 
determine what objects the wmm might desire. 

There is one obvious hint that the player re- 
ceives: every time the player drops one of the wmm 
goodies at location wmm, that object will disap- 
pear. In other words, if the player says: 


drop incense 


and then 


carry incense 


the response will be: 


| don’t see any incense here. 


The object goes directly into the collection owned 
by the wmm. 

In addition the player must remove everything 
else from the wmm’s sight in order to make him 
happy. That is, whatshere[wmm] must be the 
empty set of objects. This may be the most difficult 
part of the problem to solve. The wmm gives only a 
vague hint. In the descriptions database, the de- 
scription corresponding to $wmmharp contains an 
exhortation to take certain items from the wmm’s 
sight. 


The Secret Word 


When the wmm has all the objects he desires 
and the wmm location is bare of all other trash, the 
wmm will reveal the “secret word.” The word is 
wadda. The player must return to the cellar and 
then say 


wadda 


in order to open the hatch leading to the fountain of 
youth. Once that has been accomplished, going 
down from the cellar brings you to the fountain and 
wins the game. If you try to say “wadda” before you 
have made the wmm happy, your guide will refuse 
to understand. This is controlled by a Boolean vari- 
able saidwadda. It may only be set to true when the 
canopen problem has been solved. 


Preconditions 
(wmmhas= wmmwants) AND 
(whatshere[wmm]= [ ]) 
(location = cellar) 
Command given is: “wadda” 


Postcondition 


canopen = TRUE 


249 


Other Techniques Used in Adventure 3 


As in the other adventures I have presented, there 
are a number of ad hoc Pascal coding techniques 
used in Adventure 3. This chapter is devoted to 
explaining why they are there and how they work. 


PUTTING MORE INTO THE DATABASE 


The descriptions database for Adventure 3 
contains more than just descriptions of locations. 
The enumerated type rooms of Adventure 2 has 
become the type descs in Adventure 3. The iden- 
tifiers in the declaration of descs fall into two se- 
quences: those before nowhere and those after 
nowhere. The identifiers before nowhere comprise 
the locations for Adventure 3. There is a subrange 
declaration 


rooms = start . . nowhere; 
that indicates this. The identifiers that follow 


nowhere all correspond to special descriptions 
used in Adventure 3. 


250 


Various Descriptions Involving the Trolls 


The trolls play an important part in Adventure 
3. They and their actions are described in great 
detail as the game progresses. Rather than embed 
this description in the game code itself, it has been 
put into the database. The following “placenames” 
correspond to troll descriptions: 


@ trhungry—When you arrive at the trolls’ location 
carrying the bones, you get this message. It tells 
you that the trolls exist and that they are eager to 
eat you. It also boldly hints that the bones are 
connected with their interest. You will know this 
for sure if you played the game before and man- 
aged to get eaten. 

@ trfollow—This is what the player is told when the 
trolls are following him or her. It describes the 
trolls and hints at the solution of the trolls pro- 
blem by continuing to mention their interest in 
bones. 

@ trtalk—The player gets only one chance to read 


this message! It describes the player’s demise 
upon reaching the trolls’ location without carry- 
ing the bones. 

@ tgablure—When the player lures the trolls to 
mtcaveta, he or she will still be carrying the 
bones. This is the big fat hint to drop them there. 

@ tgabbones—If the player succeeds in luring the 
trolls to mtcave1 a and is smart enough to drop 
the bones there, he or she is rewarded with this 
message. It describes the trolls sudden switch in 
interest from you to the bones you have dropped. 
It also hints at the tooth problem by mentioning 
breaking teeth. The player should connect this 
hint to the wmm hint later about troll’s teeth. 


Descriptions Involving the 
Wise Man of the Mountain (wmm) 


There are many entries in the Adventure 3 
database that involve the wmm. Most of them are 
things that the wmm may say to the player at some 
point during the play of the game. Here is a sum- 
mary of the wmm related entries: 


@ wmmsp1, wmmsp2, wmmsp3, wmmsp4, 
wmmsp5—These are five “spiels” of varying 
length that the wmm gives the player on second 
and subsequent visits to his domain. They con- 
tain hints, varying in their degree of obvious- 
ness, about the objects that the wmm truly de- 
Sires as treasures. 

@ wmmhello—This is the initial speech delivered 
by the wmm when the player first visits the wmm 
location. It is a general description of the fact that 
the player should find various treasures and de- 
liver them to the wmm. It deliberately misleads 
the player (or tries to) into thinking that the 
treasures desired are of the conventional variety. 

@ wmmharp—This speech is delivered as soon as 
the player manages to bring enough bogus trea- 
sures to the wmm. The number of fakes de- 
livered so far is recorded in the variable 
wmmcount. How wmmcount is kept up to date 
is discussed later in this chapter. If wmmcount is 
greater than or equal to three, this message is 
displayed. It gives the player the rude surprise of 
finding out that what the wmm means by trea- 


sures is not the usual meaning. It also contains a 
hint about the trolls tooth. 

@ wmmblab—When the player succeeds in 
gathering all the wmm’s true treasures and de- 
livering them to location wmm, the wmm spills 
his secret. The location of the fountain of youth 
is dispensed along with a magic word for opening 
the doorway to that fountain. 


The Fountain Description 


The desc identifier fountain corresponds to 
the description of the fountain of youth. This is 
displayed only at the very end of the game when the 
player has succeeded in opening the hatch leading 
to the fountain. It could have been made an ordinary 
location, but because the game ends as soon as the 
player arrives, it was easier to handle it as a special 
description. 


Modifications to the Show Procedure 


The addition of descriptions not corresponding 
to adventure game locations could have been ac- 
complished in another way. I could have created a 
separate database. The disadvantage of this is the 
requirement for two more open files during the play 
of the adventure game. Each file opened while the 
UCSD Pascal program is running causes a perma- 
nent requirement for RAM memory. The more 
RAM used for file, the less RAM there is available 
for coding features into the adventure. Therefore, I 
decided to extend rooms to descs as I have already 
begun to explain. This causes the show procedure 
to change, as I shall now discuss. 

The show procedure simply prints lines from 
the descriptions database on the player’s screen. In 
Adventure 2, these descriptions were limited to 
adventure game locations. In Adventure 3 they can 
include descriptions of troll activity and speeches 
given by the wmm. Recall from Chapter 16 that 
show only displays the description of a location 
when the variable chgloc is true. During the de- 
velopment of Adventure 3 situations arose when 
chgloc was still false, yet a description other than a 
location description was needed by the Pascal code. 
This required that show be changed in order to 
force out nonlocation descriptions when chgloc was 
false. 


251 


The solution involves the use of the intrinsic 
function ord. The ord function may be used to com- 
pare the position of two identifiers in the declara- 
tion of an enumerated type. If one identifier appears 
after another in the declaration, the ord value for 
the second identifier will be greater than that for the 
first identifier. For the descs enumerated type, all 
identifiers corresponding to special descriptions 
come after the identifier nowhere. The ord func- 
tion can detect this as follows: 


ord (where > ord (nowhere) 


This is one of the additions to the tests used in 
show to determine when to print a description and 
when not to print it. 

An unexpected side effect of nonlocation de- 
scriptions also turned up in Adventure 3. Part of the 
act of showing a location to the player is showing all 
the objects that happen to be there. In Pascal terms, 
the procedure showgoodies, which describes the 
objects present, is invoked from inside the proce- 
dure show, which describes the unchanging physi- 
cal appearance of the location. When nonlocation 
descriptions were added, the listing of objects 
present in certain locations started to be repeated. 
Whenever there were objects present in a location 
and a nonlocation description was printed while the 
player visited that location, the objects there were 
described twice. For example, the first time at 
location trolls, the trolls location description was 
given; then the objects present were listed; then 
the trtalk message was printed; then the objects 
present were listed a second time. 

The solution to the multiple showing of objects 
at a location also involved the use of ord. The idea 
is—never call showgoodies for a nonlocation de- 
scription. This is accomplished by placing the call 
to showgoodies inside an IF test that uses ord: 


IF ord (where) < ord (nowhere) 
THEN 
showgoodies; 


A Few Pitfalls to Watch Out For 
In the process of making rooms into descs 


252 


some problem areas were noted. Here isa brief list, 
so you can watch out for them when you start writ- 
ing your own adventure games. 

Value Out of Range Errors. There still 
exists a rooms type in Adventure 3. It is declared as 
a subrange of descs: 


rooms = start . . nowhere; 


If you declare variables of type rooms—for exam- 
ple, loop control variables for loops that range over 
the values in rooms—you must be careful not to 
assign those variables values that are “out of 
range.” For example 


VAR 
r: rooms; 
BEGIN 
FOR r := start TO fountain DO 


Because ord (fountain) > ord (nowhere), fountain 
is not a legitimate value of type rooms. The Pascal 
compiler unfortunately does not check this in the for 
statement. The check does occur when the program 
runs, however. The assignment r := fountain 
causes the program to come to a screeching halt. 
The moral of the story is—Be careful! 

MAKEDESC Considerations. The source 
for the adventure game database must be handled 
carefully. The main requirement is that there must 
be a placename description line for every identifier 
in the descs enumerated type. Even if an identifier 
has no description actually associated with it, there 
must be a placename description line for it. In par- 
ticular, nowhere must have its own placename de- 
scription line. If this rule is not followed, the index 
to the database may get out of whack. That is, the 
index entry for a given identifier may point to the 
wrong description in the database. 


CODING TECHNIQUES TO GET 
AROUND UCSD LIMITATIONS 


There are two places in the implementation of 
Adventure 3 where the programming make look 
arbitrary at first glance. The first is the main pro- 
gram block: 


IF ord (location) < ord (steep1) 
THEN 
t1 (location) 
ELSE 
t2 (location) 
{ END IF ord... } 


The second occurs in the initialization part of the 
advdata unit: 


BEGIN 
init1; 

init2; 

END; 


In both of these places there are two proce- 
dures invoked. It might seem that either a single 
procedure or direct inline code might have been 
adequate in both cases. The explanation of the two 
instances of strange coding is that UCSD Pascal 
places a limit on the size of a procedure. If the 
amount of code generated by the procedure exceeds 
this limit, the compiler issues the error message: 


Error 253: Procedure too long 


and refuses to create a.CODE file for the program 
or unit being compiled. 

When a procedure becomes too long, the solu- 
tion is to break it apart. The result may be confusing 
when the program listing is read by someone later 
on. When you get Error 253 from the compiler, 
however, you are much more likely to be frustrated 
than confused. 


TRICKERY IN showgoodies 
The showgoodies procedure, which lists the 


objects present in a given location, has been im- 
proved as compared to the similar procedure in 
Adventure 2. The guide uses good English gram- 
mar in the descriptions. This is accomplished by the 
use of a few extra set variables declared and ini- 
tialized in the cmds3 unit. The variables are called 
plurals, somes, and useans and are all of type 
collection, that is, SET OF objects. The objects in 
Adventure 3 are divided into these three sets to 
indicate which pronoun to use when mentioning the 
corresponding object. For example, the object keys 
belongs to the set plurals. When keys are present in 
a given location the guide will say 


There are some keys here. 


The object leather is in the set somes. When de- 
scribing it, the guide will say 


There is some leather here. 


The object axe is in the set useans— in fact, it is 
currently the only object in useans. The guide will 
say 


There is an axe here. 
when describing it. Finally, the object scroll be- 
longs to none of the sets. The guide will describe it 
with 


There is a scroll here. 


This is a little tough, admittedly. However, the 
existence of Pascal set variables makes it so pain- 
less to implement that I couldn’t resist. 


253 


Writing Your Own Adventures 


If you have read this book carefully and studied the 
examples presented, you should now be ready to 
write your own adventure games. You have all the 
tools necessary, and the only limit now is your own 
imagination and perseverance. The purpose of this 
final chapter is to present several final suggestions. 
I shall discuss possible extensions to the adventure 
game programs in this book, especially Adventure 
3. Finally, before saying farewell, I present a possi- 
ble step-by-step approach to the game writing pro- 
cess from start to finish. 


EXTENDING ADVENTURE 3 


There are several directions you might take if 
you were interested in extending Adventure 3. You 
may think of ideas beyond those presented here. 


@ Adding Locations 
Adventure 3 has no maze. You might add 
one, starting somewhere in one of the two caves. 
You might add more mountainpeaks—currently 
all the peaks contain important objects of inter- 


254 


est to the wise man of the mountain. This is 
somewhat of a giveaway to one of the central 
problems of the adventure. Adding more peaks 
and possibly relocating some of the fake or trash 
objects to those peaks might make the game 
more interesting. You might also add more 
nooks and crannies in the general locations of the 
game. A final possibility would be to add rooms 
inside the monastery—either as a diversion, or 
possibly containing one of the wmm treasures. 


@ Adding Problems 

There are only a few problems in Adven- 
ture 3. Adding more would definitely make the 
game more interesting. It would also be a good 
opportunity for you to practice your problem in- 
venting and problem implementing skills. There 
is more on possibilities in this area later in the 
chapter. 


Extending the Map 


When you add new locations to an adventure 
that uses a descriptions database, you have several 


steps to perform and several things to worry about. 


1. The new locations must be given names and 


added to the descs enumerated types definition. 


They should be placed in the section of the 
declaration corresponding to actual locations, 
that is, before location nowhere. 

2. For each new location, a description must be 
added to the database. To accomplish this, the 
MAKEDESC source file must be updated to 
include a placename instruction line for each 
new location, followed by the text of the de- 
scriptions. 

3. The added descriptions must be placed in the 
MAKEDESC source file in the same position 
and order as the corresponding names were 
added to the descs enumerated type. 

4. The MAKE80 generator must be run on the 
updated MAKEDESC source in order to gener- 
ate a new database. 

5. All the units and main programs of the adventure 
must be recompiled. This is because the descs 
types is in the interface of the lowest level unit 
in the program—everything else uses that unit. 

6. The adventure game must be relinked after all 
its component parts have been recompiled. 


Adding Problems to Adventure 3 


There are any number of possibilities for 
adding problems to Adventure 3. New problems 
should fit in with the spirit of the game. They should 
fit with the setting and the feel of the game. Here 
are a few possibilities: 


1. The shout command, so far, is used for atmos- 
phere only. You could extend it in two ways, 
both of which could pose problems for the 
player. 

First, you could cover one of the important 
objects with snow. It would only be revealed if 
the player asked the guide to shout in its vicin- 
ity. Some of the descriptions already used in the 
game have hints to the effect that shouting might 
cause an avalanche. The second change to the 
shout command would, in the wrong places, 
cause an avalanche that buries the player and 


ends the game. The two new problems are thus 
to discover the buried object and to avoid being 
buried in the process. You might modify some 
more of the descriptions to insert bolder hints 
regarding shouting. 


2. The only “roadblocks” in the current mountain 


adventure are the trolls and the locked door 
leading to the cave under the cellar. You could 
easily block other important passages. You 
might have a pile of boulders blocking the pass to 
an important peak and so on. How such problems 
would be solved by the player, I leave to your 
imagination. 

3. You could make the tooth problem more difficult 
by making the tooth appear only once. Then 
after a certain number of turns, it would go away 
forever. 

4. You could require some sort of toll in order to 
gain access to the wmm. The first time you visit 
his location, if you cannot provide the toll, you 
will be rudely dismissed and unable to regain 
access to the wmm until you do provide it. If you 
fail to bring the toll the first time, you will be 
unable to score the maximum number of points. 

The toll could involve almost anything. For 
example, you might have to discover some se- 
cret documents inside the monastery and de- 
cipher them to find a magic phrase that you must 
intone in the wmm’s presence. I again leave to 
your imagination other possibilities. 


USING A SKELETON ADVENTURE 


Using the term skeleton adventure might bring 
to mind another game based on Halloween themes. 
However, in this case I have a different interpreta- 
tion in mind. A skeleton program is a program out- 
line, or partial program, that may serve as the 
starting point for writing many different incarna- 
tions of the same kind of program. By paring down 
the code for Adventure 3 to just those parts that you 
are likely to use in every adventure game, you will 
arrive at a skeleton adventure. When you begin 
writing a new adventure game, you will copy the 
skeleton adventure and use it as a starting point. In 
each new game, the code you add will differ as will 
the game you create. 


255 


Adventure 3 consists of a main program and six 
units. Each of these components can form the basis 
of part of a typical skeleton adventure. The main 
program retains the idea of a repeat statement with 
a CASE nested inside it. The case statement is 
based on the rooms enumerated type and invokes 
either the travel procedure, to cause a change of 
location, or a location procedure, to handle special 
action at the location, or a location procedure, to 
handle special action at the location in question. 
The form of the code is always the same; only the 
specifics vary from game to game. 

Likewise, many of the procedures and func- 
tions of the units cmds1, cmds2, and cmds3 will be 
used by every adventure game in the process of 
handling commands. The specific commands im- 
plemented will vary, but the general outline of the 
code that handles them will remain the same. The 
same sort of reasoning applies to the units locs, 
probs, and advdata. 


A Sample Skeleton Adventure 


In Appendix A I present a listing of a skeleton 
adventure based on Adventure 3 and its division 
into units. You may use this skeleton as a basis for 
your own games or you may decide to create your 
own skeleton based on the techniques presented in 
this book and others you invent for yourself. 


A SYSTEMATIC APPROACH 
TO WRITING ADVENTURES 


Above all else, writing adventure games 
should be fun. The whole idea is to enjoy yourself. 
Create interesting games and let your friends and 
family try to solve the problems they pose. You can 
add to your enjoyment by having a “system.” Ap- 
proaching the software development process in an 
organized fashion can help you produce more games 
ina shorter period of time, with fewer bugs in them. 

The following checklist is only a suggestion. 
You will probably deviate from it when you develop 
your own approaches to adventure game writing. 
But when you do, develop your own checklist. Then 
use it when you write your next game. See if you 
don’t finish sooner and enjoy yourself more. 


256 


The Adventure Game Writer's Checklist 


1. Draw the adventure map. Use a big sheet of 
paper and leave room for notations and additions 
and subtractions. You might create fragments of 
the map on smaller pieces of paper and transfer 
them to the larger map when you are satisfied 
with them. I use artists drawing paper bought in 
an office supply store for this purpose. Large 
size computer printout paper is also good. 

Remember to use the tricks I covered ear- 
lier. Note places on the map where problems 
need to be solved. Number the problems and 
either write brief descriptions of them beside 
the numbers on the map or somewhere else. 
Write down enough when you think of the prob- 
lems so that you won't forget them later on. 

2. Write the descriptions. When you have com- 
pleted the map or at least derived enough of it to 
have a good idea of the overall flavor of the 
adventure game, sit down and start writing the 
descriptions of the game locations. This can be 
one of the most enjoyable parts of the game 
writing process. You have a chance here to pre- 
tend you are writing an adventure thriller. Exer- 
cise your imagination. Pretend you are actually 
in the adventure game and try to describe the 
places as you imagine they would be. 

Remember as you write descriptions to in- 
clude hints. Problems that are difficult to solve 
may involve the player’s discerning these hints 
and using them to deduce solutions. Try to make 
the hints substantial without being obvious. 

3. Create the problems. I have belabored this as- 
pect of adventure game writing enough. You 
know what to do and how to do it. Just one last 
time, I will say, “Be creative!” 

4. Decide on the objects for the game. Many ob- 
jects may be associated with problems, so you 
have a head start on this step. Decide which 
objects are essential to the game play and which 
are merely decorative or misleading. Try to 
keep a balance between the two categories. 

Think about where you want the various 
objects to be located at the start of the game. 
Make notations on your map indicating this in- 


formation. Decide whether you want any trick- 
ery like that associated with the troll’s tooth in 
Adventure 3. If you do, think about how you will 
implement the tricky parts. 

. Decide which commands the guide will recog- 
nize. I have barely scratched the surface of this 
subject. There are endless possibilities for in- 
teresting commands. Make sure that your in- 
tended commands won't be too hard for the 
command handling code to decipher. 

. Write the Pascal code for the adventure. Com- 
pile and link it and debug the program. When this 
step is finished, you will have your completed 
adventure. Again, you may start with the 
skeleton source files provided in Appendix A, or 


you may code “from scratch” if you decide upon a 
radically different organization for your pro- 
gram. Unless you are a very experienced pro- 
grammer, the first approach is probably best for 
your first few games. 


FAREWELL 


I hope you enjoyed reading this book, and I 
hope you will enjoy writing your own adventures 
even more. I enjoy playing adventure games, but I 
think writing them is much more fun. If you have 
any comments or further interest in adventure 
games, feel free to send them along to me. May all 
your adventure games run the first time. 


257 


Appendices 


Appendix A 
Skeleton Adventure Program 


In this Appendix I provide you with the listings of 
the skeleton adventure game as discussed in 
Chapter 30. These listings are derived from Ad- 


SKELETON ADVENTURE MAIN PROGRAM 
PROGRAM adventures 


venture 3 and are organized into a main program and 
several units. The main program and each unit has a 
separate listing. There are a total of 7 listings in all. 


Ck S4+K) 
USES applestuff, More “uses” instructions. 

(X#uskl.codek) advdata, 
(X#uprobs.cadex) probs, For these and additional “uses” instructions 
(xXtucmds3,.codek) cmdss, which appear on pages 262, 263, 273, 274, 282 
(xX$ucmdse2.cadexk) cmds2, and 283, refer to “A note about disk Drive 
(kK$ucmdsl.codek) cmdsl, numbers in file names and commands” on 
(kK$ulocs.codek) locs; page 3 of this PDF. 

BEGIN 

REFEAT 


visitedClocatioang 


261 


visitedClocatian] + 13 


show (location); 


es ee rn ea rn ee em es Sen eR eS RT eS os 


the following CASE statement caontroals travel 
within the adventure. it contains ane case 
label for each place in the adventure game. 
the procedure “travel’ is passed the 
destinations in each of the six directions: 
Ma Se@.Walte,d which are reachable from the 
lecation represented by the case label. if 
the lacation needs "special handling", then 
a location procedure ptxyz? is invoked 
instead (xyz is the location identifier). 
if there are so many locations that this 
CASE causes the procedure to become too 
large, then a trick like that in adventure 
3S must be used to split the CASE into two 
ar more parts. 


CASE location OF 


starts travel (nowhere, nowhere, nowhere, 


nowhere, nowhere, nowhere) s 


END { CASE location OF}; 


UNTIL done: 


END. 


SKELETON LOCATIONS PROCEDURES UNIT 


($543 


UNIT locss 


INTERFACE 


USES applestuff, 


{tuski.code} advdata, 
{$uproabs.code} probs, 
{fucmds3.cade} cmds3, 
{Sucmds2.code>} cmds2, 
{tucmdsil.cade} cmdsl; 


{ Location procedures: ptxxx? are declared in this 


262 


bt te ke ws es 


Re het hb ty BS he 


63 43 


% 
2 


interface. A location pracedure only needs to 

exist if a given location has special handling 
that cannot be implemented in ather ways. 

See Adventure 2 and Adventure 3 far examples. 


ey ree rs 


IMPLEMENTATION 


{ Put code for the location procedures: ptxxx> here? 


BEGIN 
END. 


SKELETON COMMANDS UNIT 1 


+ 


emdsl.text 


Source Files 


Ce ae oe 
hes bet Fe, 


i 
i 
: 
i 
i 
i 
' 
t 
i 
i 
H 
i 
} 
i 
: 
j 
i 
i 
i 
H 
i 
ies | 
i 
t 
: 
i 
! 
} 
i 
i 
t 
i 
{ 
i 
i 
i 
i 
' 
at bd 


This unit contains all command processing 
suppart procedures and functions. Each 
command in the adventure is carried out by a 


procedure called ptcmd-, 


where 


feomd > 


stands 


far the name of the command, 


ks het he he ee he et 


as typed by the 


player: @.qg. pcarry 


{===> carry command. 


Some of the ptemd? procedures are located in 


this unit, 


the athers are in cmds2 and cmds3. 


Frocedures and functions like travel, 


NOWAY » 


docammand, 


listen, 


and cmdlooakup which are 


invoked in order toa recognize the command are 
all located in this unit. 


1 
RS RS ot et Re Re he ke 


i 
i 
i 
i 
i 
t 
: 
i 
t 
i 
i 
H 
{ 
i 
i 
i 


Fy Fn FN SP eS ON ER RS A Se OS eS 


{$o+3 
UNIT cmedsis 


INTERFACE 
USES applestuff, 


{Puski.cede? advdata, 
{$uprobs.code} probs, 
{fucmds2,code} cmds2, {Sucmds3.code} cmds3, 
{S$ucmds3.code? cmds3; } {Sucmds2.code} cmds2; 


cmds3 must be listed before cmds2: 


263 


PROCEDURE travel (¢ 


nloc, 
sloc, 
eloc, 
wloc. 
WULOC » 
dlocs rooms); 


PROCEDURE pecarrys 
FROCEDURE pdrops: 
FROCEDURE pshouts; 
PROCEDURE popens 
FROCEDURE pwadda; 


IMPLEMENTATION 
VAR 
dchars: SET OF chars 
Cc kk p r Q b 1 e nm S 


See if the player has solved any problems 
because of the command just executed. 


eae en ces es 


FROCEDURE ckproblemss; 
BREGIN 


{ This procedure may be used ta check 
{ indirectly for the solution of same 
{ preblems. Look at Adventure #3 far 
{ some good examples. 


END ¢ PROCEDURE ckproblems 33 


Determine which command the player has typed 


and “tail’ for command verbs with objects. 


This is the generic command lookup function. 


m 
5 
¢ 
if 
c 
. 
{ and dissect the command string into *head’ 
¢ 
cr 
. 
£ 


It assumes the existence of an enumerated 


264 


et ee het be 


hat foat 


fod tact 


het het 


H 
BS ee et he 


ws he Ls he 


type called cmds and an array of strings 
called cmdname, indexed by cmds. The string 
cmdnamelxyz] contains the string which the 
player must type in order to execute 

command xyz. 


Rt ke eet be ee 


Ce a ae he SO oie Sh 


at 


FUNCTION cmdlookup +: cmds; 


VAR 
p: INTEGER; 
lemd: cmdsy; 
BEGIN 


writeln; | Capitalize Your’ 
write (your wish is my command*> *)s; 
readin (command) 5 


p r= pos (* *, cammand)s: 


+. 


{ check for verb-object } 


IF p = 0 
THEN 
BEGIN 
head == command; 
tail s= ""3 
END 
ELSE 
REGIN 
head := capy (cammand, 1, pri): 
tail := copy (command, pti, length(command)-p); 


END { IF p = © 33 


cmdnamelnocmd)] s= heads; 
lemd : 


WHILE head <> cmdnamellemd] Da 
lemd s= suce (lemd) 


{ END DO 335 


emdloakup := lLemds 


265 


END ¢ FUNCTION cmdlookup 3; 


{- cress ogee sens sane seste teave seco svete sete oneee asso aovee cones snese sater onene steuh waver teatd we0ee saves anes ¢uren anene conse soars ennee anene Senne crwss sevee ontse cuptD Ss0e0 Oemme Oxmmb eosse seven scene cecee sauhd Setar crane cosee ~" 
{ p $s c Q r e 3 
if 3 
{ Calculate the number of points scored by the 3} 
{ player up to this point in the game. 3 
{ aoa see ‘ees’ Sees Sns6h essen Souan Gens @nten oparo'saeke steve esos sane ‘sess cote atmme seese eonse seuse seen sess neon chats eases epube sesse sserd sees asaee ewes iQnimr ets sesee cones S0be0 seete aneee seve areas trees sntes eben sebes Sanne ese } 
FUNCTION pscore s INTEGERS; 
VAR 
re rooms; 
keepscores INTEGERS: 
BEGIN 
keepscore := Of; 
FOR r i= start TO wom leftover from Adventure 3; 
DO ; change to first and last room names 
IF visited{fr] > 9 
THEN 
keepscore := keepscore + S; 
IF saidwadda 
THEN 
keepscore := keepscore + 


(350 ~ ord (fountain) ~- 24); 
IF location = fountain 
THEN 
keepscore := keepscore + 253 


pscore := keepscores 


END { FUNCTION score 3}; 


1 i 5 t e n 


Dispatch calls to the command execution 
procedures ( ptcmd?> as described in the 
header comment far this unit.) 


ere ere et et 
kt et Os ew Ls es 


PROCEDURE listens 


266 


‘saidwadda’ 

and 

‘location = fountain’ 
are leftover 
achievements 

from 

Adventure 3 


VAR 


lemd: cmds; 


PROCEDURE lscores 


BEGIN 
writeln (If you should quit now, your score would be 
writeln (pscore, * points of a possible 350.*)+3; 

END ¢ FROCEDURE Ilscore 3}; —~— 


Leftover from Adventure 3 
FROCEDURE lquits 


VAR 
ches CHAR s 
fhold response for quit confirmation} 
BEGIN 
writeln 
readin (ch); 
IF (ch = *“y"*) OR 
THEN 
BEGIN 
writeln (you would have scored *, 
exit (FROGRAM) ; 
END {IF (ch = *y’) . 
END < PROCEDURE lquit 


Care you sure you want to quit?")s 


(ch = "y") Capitalize ‘Are’, ‘You’, and the second ‘Y’ 


pscore, 


; 
33 


ae 
7§ 


BEGIN 


REPEAT 


turns 
lemd 


s= turns + 1s 
r= cmdlookups; 


CASE lcemd OF 


a 


take, 
carry: pearrys 
drop: pdrops 
help: phelps; 
invent: pinventorys Expand list or eliminate commands as needed 
Score, 
tally: lscores;s 
looks plook; 
quits Lquits 
nocmd: 3 

END ¢< CASE lemd OF 33 


267 


s 
4 


points.” )y; 


ckproblemss 

{ See if any problems were solved by the } 

{ command issued by the player. 3 
UNTIL lemd = nocmeds 


END {¢ PROCEDURE listen 33 


i 
H 
; 


Call listen in a loop. When the length of 
head is greater than zero, the user has given 
a travel command so return the first letter 
of “head* to the caller. 


he Rt eS he het he 


es er es ee ee en es 
: : 
tet hee 


FUNCTION docommand :s CHAR; 
BEGIN 


head r F] 
tail a 
chgloc : 1 


REPEAT 
listen; 


UNTIL length (head) = O¢ 
docommand := headCid; 


END € FUNCTION docommand 33 


me me me te ete et te test eee te ai ee et tte ese nt tt ear nse nts ns tm ese et tne tn tn en ne as se 


if w h i c h w a y 
£ 
{ Determine which way the player wishes to qo. 


{mm nim mee met mete ne te te ere mee tte ee et ne se Set te tines Sst ae tn se ht te ne ts sf ne 


wt wd Re het ke 


FUNCTION whichway : directions; 
VAR 

ch: CHAR; 

ww: directions; 
BEGIN 

REFEAT 


268 


ch := docommands 
whichway #= x4 


CASE ch OF 
oa ae IF (head = *n’*) OR (head = “north’*) 


THEN 
whichway i= ms 


7s" 4 IF (head = “s*) OR (head = *smuth’*) 
THEN 
whichway 2 S% 
"ey IF (head = *@e*) OR (head = “*east") 
THEN 
whichway := @5 
Pw: IF (head = *w’) OR (head = “*west*) 
THEN 
whichway #= ws 
"urs IF (head = *u’) OR (head = “*up") 
THEN 
whichway 2= us 
"d's IF (head = *d*) OR (head = "down" ) 
THEN 
whichway := ds 
"q's { empty for now 35 


END ¢<¢ CASE ch OF 33 
UNTIL. ch IN dchars; 
writelns 


END ¢€ FUNCTION whichway 33 


[rm mec ee te ee ee tee oe ae te ese ea te te ae aah Sn ne et mt ste tnt snes senses ne ese ttn tn eee tne te sens teat ene 


3 
¢ n Q w a y 3 
{ } 
u 


PROCEDURE noways 
REGIN 


269 


writeln: J Capitalize ‘It’ 
writeln ("it is impossible to go in that direction.’); 
chgloc := false; 


END { PROCEDURE noway 733 


A cco naa ibe mabey Seas arenas aera ass esc 608 apenas nasa ceana ous geee sons nen as So tess eae ean foun eee nas Ne 
oo 2 
£ t r a v e 1 } 
f 9, 
. P 
{ Handle travel to the next location. The } 
{ possible destinations fram the current room 3} 
{ or adventure location are passed to travel 3 
{ parameters. The value “nowhere” means that 3} 
{ there is no way to go in the corresponding } 
cr % 
.o ae 


direction. 


et 


i 


rs 
4, 


FROCEDURE travels 


PROCEDURE newloc (loc:descs); 
BEGIN 


IF loc = nowhere 
THEN 
noway 
ELSE 
REGIN 
location := 1 
ch@lac = trues 
END <{ IF lac = nowhere }3 


END {¢ FROCEDURE newloc 33 


PROCEDURE wingame; 
BEGIN 


show (fountain) 3 — leftover from Adventure 3, as well as “350” below 
writeln (* You scored a total of *, pscore)s 
writeln ¢(* points out of a possible 350.")s; 
exit (PROGRAM) : 

END ¢ PROCEDURE wingame }3 


BEGIN CKKKKK to roa vee | kKKK} 


270 


CASE whichway OF 


ns newlec (nloc)s 
ss newloc (sloc)s 
es newloc (eloc); 
we newloc (wloc): 
us newloc (uloc)s 
ds newloc (dloc) + 


x: BEGIN 
writeln (*°I do not understand that.’)s: 


writeln (’FPlease try another command’) : 
END; 


END {£ CASE whichway OF 33 


END { FROCEDURE travel 13 


: 
! 
i 
} 
i 
i 
i 
H 
i 


Lied 


p c a r r y 


Implement the carry command. Call the 
function *objleokup” ta determine which 
object (if any) has been requested. Then 
call “*ckgoodies* to see if that object is 
present in the current location. If so, the 
object is added to the set “stash* and also 
removed from the set “whatsherellocation]’. 


race es rn sy 
i 
tet Met Bt et bw ht 


i 
i 
f 
H 
i 


aie he ain oe 
Bs tt ed Le bet 


FROCEDURE pcearrys 
VAR 

it: goodies; 
REGIN 


it = objlookups 


IF NOT ckgoodies (it) 
THEN 
BEGIN 


write ("I don*"t see any “*)5s 
write (tail); 
writeln (* here.” )3 


END 
ELSE 


271 


BEGIN 
writeln ("Ok"): 
stash := stash + Citi; 
whatsherelLlocation] := 
whatsherelLlocation] - Citds 


END ¢€ IF NOT it IN wan 35 


END { FROCEDURE pearry 73 


{ dt sats seein onhin nei steel otib sb Gants snd: sondn|etosn einen ees sobie to 9s sn mt ne et ut te mt tt tsa ts mt te ns ma ems emt an 4 ae se Ne 
{ p d r QO p 3 
¢ 3 
{ Implement the drop command. similar in 3 
{ action to the carry command (q.v.). 3 
me oe me ee eet me se te se mie ate re ates ne etm ns si ns te ee mt tn ee ess et ae et te tes me 


PROCEDURE pdrops 
VAR 

its goodies: 
BEGIN 


it = objlookups 


IF NOT (it IN stash) 
THEN 
BEGIN 


write ("You are not carrying any *)¢s 
writeln (tail); 


END 
ELGE 
BEGIN 


writeln (?Ok*)s; 
stash := stash ~- Litids 
END: needs an END; statement for IF NOT (it IN stash) 
END ¢ FROCEDURE pdroap 33 
Additional commands from page 267 would go here. 
BEGIN 


dchars := 


272 


t* a’. *H* 5 a6” e’, 7 yw* a’ ae a: ae eet 


‘q’ is unimplemented, see page 269. ‘x’ is a delimiter, or “sentinel” 
END. 


SKELETON COMMANDS UNIT 2 


H 
i 


| 
| 
i 
i 
i 
A 
! 
i 
i 
i 
H 
i 
3 
i 
i 
i 
i 
$ 
tat 


t., . 
{ } 
Lf a 
co é 
if cm ds 2 uom iat 3 
£ 2 
Ss - 
{ This unit cantains the procedure phelp. The +} 
{ help command needs no access to data in unit +} 
{ "*advdata’. It dees use procedure *show’, 3 
{ however, and hence USES unit cmds3. > 
¢ seme sesso seves seute seen nue sess costs svete some sense sense seete sees secon nonue cesen cones tones eevee wenen ettee anees enute pés00 sents seuss Sorse sense cesee sees ensst Gnvue eeses sete seeee G068® Sones cette sees année S1688 seuss ettte sense seems cutee + 
C$a+3 


UNIT cmds2s 
INTERFACE 
USES applestuff, 
{$usk1l.cede? advdata, 
{ucmds3.code} cmdsi; 
PROCEDURE phelp; 
IMPLEMENTATION 
VAR 
asked: INTEGERS: 


PROCEDURE phelps 
BEGIN 


IF asked = 2 
THEN 
show (helpspiel) 
ELSE 
BEGIN 


writeln ("Help is on the way’): 
asked := asked + 13 


273 


END; 
END { FROCEDURE phelp 33 
BEGIN 

asked s= OO; 
END. 


SKELETON COMMANDS UNIT 3 


source file: cmds3.text 


; 
4% 

r 

. 

c 

\ 

{ 

{ This unit contains procedures implementing 

{ commands. The particular commands implemented 
{ herein need access to the unit advdata, 

if 

i 

% 

C 


not to the unit “probs’. 


ad 
in 

+ 
we 


UNIT cmds3s 
INTERFACE 


USES applestuff, 


{#uski.code} advdatas 


TYPE 
directions = (Ng Sa Ou Walla aX) G§ 
cmds = (carry, drop, 
take, tally, 
pname = STRINGL401; 
storyline = STRINGCSOI; 
byte = O,.2008 


274 


Rs eet Lo 


SCOres 
quit, 


but 


hs he 


be tet ht Go 


a 


invent, 
nocmd) s 


whichsect 


placerec 


(indexsection, descsection);: 


RECORD 
CASE sections whichsection OF 
indexsection: ( tableentry: INTEGER) ; 
descsection: ( Names pnames 
id: INTEGER: 
dbegins INTEGER; 
dend: INTEGER: 
link: byte); 
ENDs 
VAR 
xfiles FILE OF placerecs; 
narrates FILE OF storylines 
places: ARRAY ECdescs] OF placerecs: 
commands: STRING; 
head: STRING; 
tails: STRING; 
cmdnames ARRAY Ccmds] OF STRING: 
chaloc: BOOLEAN: 
{ has player moved since last cmd’? 3} 


FROCEDURE pinventorys 
FROCEDURE plooks 

FROCEDURE show (where: descs) : 
PROCEDURE showgoadiess: 
FUNCTION objlookup: goodiesys 


FUNCTION ckgoodies (its goodies) : BOOLEAN; 


IMPLEMENTATION 


VAR 
rs descs; 
plurals: SET OF goodies; 
SOMES: SET OF goodies; 


275 


useans: SET OF goodies; 
{ for benefit of showgoodies 3 


rn 
i 
! 
‘ 
i 
i 
! 
f 
1 
f 
: 
{ 
i 
j 
i 
\ 
i 
| 


if 
TI 


Oo w 


Retrieve descriptions from the database 
and display them on the player’s console. 
The argument to show is af type “descs’ 
which includes descriptions of situations 
and spoken words as well as descriptions 
of locations. Show uses the ord function 
to detect what kind of description is 
invelved. The description of locations 
suppressed if the player has not changed 
locations or if the location has been 
visited recently. If the player has not 
changed location, then nothing happens. 
If the player has visited the same place 
recently, then just the short description 
is displayed. 


by hed ae ee 


AA AO Me PAN ee Ne el cl eR PA el ot 


FROCEDURE shows 


VAR 
is INTEGER: 
BEGIN 
IF (chgloc) OR 
(ard (where) = ard (nowhere) ) 
THEN 
BEGIN 
IF (visitedfClocation] = 1) OR 
C((visitedClacation] MOD 4) = ©) OR 
(ord (where) * ord (nawhere) ) 
THEN 
BEGIN 


WITH placest€wherel] DO 
REGIN 


FOR i := dhegin TQ dend Da 
BEGIN 


seek (narrate, i)s 


276 


Rt het bet BS OS RS A LS A te bs Rs ks 


get (narrate); 
wreite (narrate™)s 


END € FOR i 8 wan F3 


END { WITH placestwhere] 33 


END 
ELSE 
BEGIN 


write ("You are ")¢5 
writeln (placesCwherel.name) ¢ 


END { IF visitedClacation] = 1 


PROCEDURE showgoodiess 
VAR 


labj: gondiess; 
BE G3 Ni 
FOR lobj := nuggets TO noaobj DO 
IF lobj IN whatsherellocationg 
THEN 
BEGIN 
IF lobj IN plurals 
THEN 
weite ("There are same *) 
ELSE 
IF lobj IN somes 
THEN 


in 


ma 


END { IF chgloc 33 

IF ord (where) «< ord (nowhere) 

THEN 

showgoodiess: 

END { FROCEDURE show 33 
Lm con eevee veves sesen seven seees eeu seene ssenw sents samen eevee seven anon seeee steed conte stsee conen cunts coven coeun svees stunt C0088 O008e orntt HeOtD evese svete sttt® stess stete Sune ste cites atnin Seuss anaes ceser eutte cutee 
{ s h oF w qooadiéeoe 
{ Print a list of the objects present at the 
{ current adventure game locatian. 
[em mee mim me ns mess esteem nes eee tne sm ut ses mn ts me stm out meme sey meee nen neses mee se nome te ete tt mat me tt mim er ees mee 


bs he et be 


Le tes 


277 


write ("There is some *) 
ELSE 
IF lobj IN useans 
THEN 
write ("There is an *) 
ELSE 
write ("There is a *) 
{ END IF lobj IN useans } 
{ END IF lob3j3 IN somes } 
{ END IF lobj IN plurals 3}; 


write (objnamelLlabjd)s: 
writeln (* here." )4 


END { IF lobj IN whatshere... } 
{ END FOR lobj s= wa. 34 


END € PROCEDURE shoawgoodies 33 


{ sus soak sbd sanen esc nes suse shane oun Set esas samen see anes ates ts dete cuacn Sasa asus eng eke Sounds ea osnan aante pis avees oimna Gases anene ene aesse| Somas ais qusna inan eases gents spe 3} 
{ a b j 1 Q a k ul p 3 
t + 
{ Determine if the name typed by the player } 
{ in a carry ar drop command is the name of 3 
{ any object actually part of the game. If } 
{ so, return the internal value of the } 
{ abject in question. + 
{ es pert ee st ee sn ae ene ene et snes nese ne ee ht te tes tne meses t anesne ee ans te sne pn es sam tens tnt ns mts eae mo te ne Ye 


FUNCTION ob jlookups 
VAR 

lobj: goodies; 
BEGIN 


ob jnamelCnoobj] := tails 
lobj := nuggets; €—— ‘nuggets’ is leftover from Adventure 3, 
it should be replaced with first object name. 


WHILE tail <> objnamellobjJ DQ 
lobj s= succe (lobj)s 
ebjlookup := labijs 


END ¢ FUNCTION ob jlookup 335 


Ch ae Sh 
Mm 
— 
0 
S 
a 
mt 
By 
HH 
het es 


{ See if an object is present. 
P 
o eae , Seow S Reiner! 


he ke 


FUNCTION ckqoodiess 


BEGIN 
IF it IN whatsherelLlacationd 
THEN 
ckgoadies := true 
ELSE 


ckqgoodies := false 
{ END IF it IN wan 33 


END € FUNCTION ckobject 33 


{ om en ry ceoee cates ont Reng aia i ai js ose: basen wend i pom sos ages ston 09 } 
if p 1 c Qa I + 
{ Implement the look cammand. -This forces 3} 
{ out the full description of the current 3 
{ lacation. The variables chglac and 3 
{ visitedCLlocatiaon] must be temporarily + 
{ reset in order to accomplish this goal. 3 
{ oosen euite aston cased sabe eaten thse seen Skee otibo eabgn des ionsen shane] tieoe' amity) stucs sawn, diabé bas lea /eaais Sects sew cess iceman decal cue Sends srw baw vee Sewh sbteo’ Sages exten onsee'esuem Sinbaraeb@ sobes coins ~ 


PROCEDURE plooks 


VAR 
savchqs BOOLEAN: 
Savisits INTEGER: 

BEGIN 
savchg := chgloc; 
chgloc = true; 
Savisit := visitedClocationg; 
visitedClocation] := 1; 


show (lacation) ; 


VigsitedClocationd : 
chgloc t= savchgs 


END ¢ PROCEDURE plook 3; 


- ‘ = yen oe a scat ede asia gan. Saban ca ay 
for rae triennial SU nl aie od nae a aa cin bee int enian ses} 
{ p i n Vv e n t ta) r y } 
{ 4, 

a 


279 


{ Implement the inventory command. Frint 2 
{ the names of all objects carried by the } 
{ 


player. 


en 


FROCEDURE pinventorys 
VAR 

lobj: goodies: 
REGIN 


IF stash «3 £9 
THEN 
BEGIN 


writelnm ("You currently holds *)5; 


FOR lobj := nuggets TO noobj DQ €—— ‘Nuggets’is leftover from Adventure 3, 
BEGIN _ replace with first object name. 


IF lobj IN stash 
THEN 

writeln (objnameLlahj I) 
{ END IF 33 


END ¢ FOR lobj := 
END ¢ IF stash ¢ > CJ 33 


END ¢ FROCEDURE pinventoary 3 


BEGIN 


Plurals s= € { Put in abjects whose names 
are plurals. 34; 

Fut in objects whose 
descriptions should begin 
with "some" : there is some 
Silver here. } J; 

Fut in objects whose names 
begin with a vowel. 3} Js; 


ey 


Ss0mMes := OC 


Cae) 


useans s:= 0 


comdnamelCcarry] 2= “carry"s 
cmdnameCdropd = “drop’s 
cmdnamelChelpd 2= "help", 
cmdnamelinvent] = "inventory’ ; 
cmdnameCtallyd r= “tally’; 
cmdnameltaked = “take’s; 
cmdnamelscore = "score; 
cmdnameClookd = Look’; 


Add or eliminate commands as needed 
280 


emdnamelCquitd 
cmdnameCnocmd i 


reset 
reset (narrate, "skel’)s; 


aa 


r= "quit? s 
:= “sentinel”; 


‘skel.x’ and ‘skel’ are placeholder names for the 


= start: 


seek (xfile, 31)4 
get (nfile)s 


pl 


acesCrJ] s= «file; 


REFEAT 


UNTIL r = lastdesc; 


{ 


‘ 


ros = succe (rds 
get (xfile); 


Places(fr] xfilevs 


lastdesc represents the last identifier in 
the descs enumerated type. 


close (xfile); 
chqloc #= trues; 


£ 
(s 


END, 


Force aut descriptian of start 3 


SKELETON PROBLEMS UNIT 


af 


ea oes eA es me rn ee en eR en eS et es en ts es 
: 


this unit contains the single functians 
solved. solved is passed an argument of type 
*"problems’*. the arqument corresponds ta ane 


the boolean expressions used to detect the 


saqlutian @f problems and camponents of 
problems. the rest of the adventure code 
uses cade likes 


IF NOT solved (luretrolls) 
THEN 


instead af using the complex expressians. 
the details of the problem expressions are 


(xfile, ‘skel.x’)3 } database files needed by your adventure. 
See “A note about disk Drive numbers in file 
names and commands’”on page 3 of this PDF. 


Replace lastdesc with the last description database entry 
as described below. (in Adventure 3 it was ‘helpspiel’) 


ws ke 


MA bs Be Re RS bs RS RS Ss ts hs ke ee ks ke he ke 


281 


{ contained here and may be changed without } 
{ forcing the rest of the adventure game code 3 
{ change as well. 3 
{ } 
econ noes Ste Sates ae alone cree ees stene mane one wanes on spt Saisie sow ans nuovo tebe esas ee es Gs cme essen ae ae nee), 
{$s+} 


UNIT probs; 
INTERFACE 


USES applestuff, 
{$uski.code} advdata; 


TYPE 


seus nese conse cours come scan weuee comes oveve eeene seste comte estes cope canes conse seecs gruee sume cece fests s0eee ouctn 060s arom= seems Gftcs cone costs camen aovae sects 


en 
i 
H 
} 
i 
i 
| 
t 
i 
i 
i 


The problems enumerated type declares 
identifiers that are used in representing 
components of problems. Each identifier 
has a Boolean expression associated with it. 
When that expression needs to be evaluated 
anywhere in the Fascal code of the game, 

the function *solved*® in the INTERFACE of 
this unit is called with the identifier of 
that expression as its argument: 


solved (probx) 


RS tet bt RS A Bw ee Re ew et et et be 


ten smete save arses sete anees sone neces samme 14406 coume seats meses onves seast seems neuen memes ovsse segue sven sees ¢tete appt nome sunte tenes regne seta tteun nent atues W190 sumed feet suse coeat seen teen sents Jenee cumne mney sense omnes 


Arm ee ee es el ra et fk 


problems = (probi, prob2, probS, prob4)s: placeholder names for problem names 


FUNCTION solved (which: problems) : BOOLEAN; 
IMPLEMENTATION 


FUNCTION sol veds 
BEGIN 


solved := FALSE; 


CASE which OF 


282 


{ Each problem identifier has its boolean 3 
{ expression inserted in the corresponding } 
{ CASE in this statement. + 
probl, 

prob2, problem names and 
prab3, boolean expression 
prob4;: solved := TRUE; are placeholders 


END {¢€ CASE which OF 3; 
END { FUNCTION solved 3; 


BEGIN 
END. 


SKELETON ADVENTURE 3 DATA 


sce: wanen goons saben Sey ones tau 0b opeee Seine, cee shen tanib, Sores name ese rden Soles \Senew susie sbopesovob shean seue’ seen soe 

\ 2 

{ source file: ski.text } 

£ $i Saas sai iba ees us as: Sones wana dann Slit nan ts 6 Gb cs ee Goes vest rapes 3 

{- sata ints nnn at iene nse yin inaie sents swose sonwe emome aiie woven Séuho Seven seert. satnk Sesto sures ot ys cd nbn cb: ibn abe nie svchg uum Ss coum Sb sinc Speeds enna eo tahoe nee 
£ 3 
os Z 
{ roo m 5s uom it 3 
£ * 
a Z 
{ Types and variables used by all other units } 
{ ain skeleton adventure. This includes the ? 
{ descs enumerated type, extended toa account 3} 
{ for some descriptions that are nat actual 3 
{ qame locations. The goodies type accounts } 
{ for all objects in the adventure and the > 
{ objname array contains names for all of } 
{ them (for use in descriptions and in 3 
t commands). - 
f *. 
. 2 
AO each caw ace lattes ta nies ee aan. me aes a ois cee Sanaa bas Ss hrs sunt ames wane aw ves ad vs ne Swe Swov ea whan Invi vedas a os abies BS 
. J 


UNIT advdatas 
INTERFACE 


USES applestuffs 


283 


TYPE 


descs = (start, nowhere, special, helpspiel):; 
rooms = start..nowheres 
goodies = (lamp, noabj); 


collection 


VAR 


lacation: 


= SET OF goodies; 


roams: 


stash: collections; 
whatshere: ARRAY Erooms] OF collectians 
visited: ARRAYEC rooms] OF INTEGER; 
ob jname: ARRAY EC goodies] OF STRINGLISI; 
turnss INTEGERS 
done: BOOLEANS 
Add more VARiables as needed 
FUNCTION rand (low, high: INTEGER) : INTEGER; 
IMPLEMENTATION 
VAR 
rs roams: 
{ loop control for initialization } 
FUNCTION rands 
VAR 
mi. Ca Ge INTEGER: 
BEGIN 
rand s= Of; 
IF low = high 
THEN 
rand := law 
ELSE 
BEGIN 
c r= high ~ law + 1; 
mx os= (maxint ~ high + low) DIV c + Ls 


284 


These three lines contain 
placeholders to be 
expanded, replaced, or 
removed as needed. 
(‘nowhere’ and ‘noobj’ 
are delimiters, 

aka sentinels.) 


myx s= mx xX 


REPEAT 
do: = randam 
UNTIL do <= mxs 
rand := low + d MOD cs 
END ¢ IF 


low = high 33 


END ¢ FUNCTION rand 3}; 
BEGIN 
locatian := starts 


ob jnameClamp] := “lamp’s 


FOR ro := start TO chasm DO 

BEGIN 
whatsherelr] :s= C4, 
visited(rd r= Og 

END ¢ FOR ro s= start one 33 

whatsherelstart] := Clampds 

stash r= Cs 

turns r= O8 

done = false; 
Expand list as needed 

END. 


low) + 


tmx oo 1)s 


Adventure 3 splits this into two procedures because it was too long for a single 
procedure in Apple Pascal. See page 253 and 236-238 for more information 


When game begins, sets current location to room named ‘start’ 


Defines full name of ‘lamp;’ as ‘lamp’, see pages 236-237 


Clears rooms of objects and clears “visited” table 
(first room to last room, start to chasm, 
which are leftover names from Adventure 3) 


Puts lamp in “start” location, see pages 237-238 


285 


Save as “A2.DB80.TEXT” (per page 83) 


See Chapters 18-23, pages 145-189, 
for instructions on making the database files. 


See page 170, “CONTINUATION FILE LINES” 
if you cannot fit the entire database into a 
single text document. 


Appendix B 
The Adventure 2 Database 


In these final appendices I provide you with the 
source for the descriptions databases used in Ad- 
ventures 2 and 3. This source is for use with the 
MAKEDESC program described in the text. 

In order to make the text fit on the printed 
page, the long lines of description used in the 
MAKEDESC source have often been split into two 


SSTART 


parts. Every place this has been done, the end of the 
first line of the resulting two lines has been marked 
with a plus sign, +. Should you type this into your 
computer system, remove the + sign from the ends 
of such lines and “tack on” the following line to form 
a single line of close to 80 characters. 


You are standing by a hole in the ground.+ 


It looks big enough to climb 
down. 
SGRANDROOM 


You are in a huge apen roam, with an immenset 


xpanse of ceiling. A dark 


passage leads west and a narrow crawl+ 


leads downward. 
$VESTIBULE 


You are in the entrance to a cave of passageways. + 


There are halls leading 


off to the nerth, south, and east. Above yout 


is a tunnel leading to the 
surface. 


286 


SNARROW 1 

You are in a marrow passage that cantinues tot 
the north. It is extremely 

narrow ta the south. A very tight crawl alsat 
leads east. A curious odor 

seeps through it. I would think twice beforet 
trying ta go that way. 

SLAKESHORE 


You are an the shore of a vast underground lake.+ 


Narrow passages wind away to 


the east and south. A small island is visiblet+ 
in the center of the lake to 

the north. 

$ISLAND 


You are on a small island in the center of at 
huge underground lake. Dark, 

frigid waters surround you on all sides.+ 

You can barely make out the 

shoreline to the south. A message is scratched+ 
in the dirt here. It says: 

"The treasure may be found in the maze.’ 

SBR INE 

You are an the brink of a steep incline.+ 

The bottom of a pit is over fifty 

feet below you. You could probably slide+ 

down safely, but I woan’t promise you 

that you could climb back up. To the west is at 
rubble-filled tunnel. A 

vampire bat just flew out of it shrieking. 

$I CEROOM 

You are in a room whose walls are formed fram+ 

a deep blue crystalline ice. To 


the north a narrow tunnel opens. From the oather+ 


end of this tunnel an ominous 

growling sound may be heard. To the east at 
sparkling luminesence emanates 

from a braad opening. To the west a passaqget 
leads back to the vestibule. 

SOGREROOM 

You are in a low roam whose walls are covered+ 
with grisly dark gouts of dried 

blood. The center af the room is dominated byt 
a firepit, which contains 

burned out coals and a long spit suspended over+ 
its center. From one corner 

emanates a horrible gqrawling noise like that of+ 


287 


some unspeakable monster 

dreaming af rending you limb fram limbt 

and making you its dinner. 

$NARROW? 

You are in a very narrow east/west passage.+ 
To the west the passage opens 

out by a lake shore. To the east it becomest 
even tighter than it is here. 

You might be able to squeeze through if you try+ 
real hard. There is also a 

strange looking alcave in the south wall that+ 
seems to open into a very dark 

tunnel. 

SPIT 

You are at the bottom of a fifty foot pit.+ 
The walls are just a hair too 

steep to climb. The pit is empty except far at 
few old dried bones. I can*t 

tell whether they are human or not!! In thet 
center of the pit is a narrow 

shinny leading further downward. 

SCRYSTAL 

You are in a dazzling hall of crystal glass.+ 
It is intensely cold here, but 

also chillingly beautiful. There are glasst+ 
formations rising from the floor 

as if they had grown there, yet delicatelyt+ 
sculptured with multi-faceted 

sides. A searing white light shines+ 
blindingly through the floor, itself 

formed from fine mirror smooth lead crystal.+ 
The light sets off reflections 

that corruscate through the room and maket 

it virtually impossible to tell 

where the room begins and where it ends.+ 
There might be an exit to the east, 

although my eyes could be betraying me. 
SRATSCAVE 

You are in a steep cavern filled witht 
shrieking vampire bats. They swoop and 

dive at you by the thousands. I would+ 
suggest a hurried departure. There are 
openings ta the west and nerth. 

$STEAM 

You have entered a roam filled with at 
stifling steamy vapor. There are 


288 


innumerable small qeysers scattered about+ 

the floor of the room, each 

cantributing its own steam ta the general mist.+ 
To the west is a dark opening 

and aneather ta the narth. Further out in thet 
middle of the roam an opening in 

the floor is barely visible through the mist.+ 
Some of the vapor seems to ooze 
through the apening as if it continued downward. 
$DEADEND 

This is a dead end. There is a cryptic messaget 
etched in the stone of the 

walls here. It reads: + 

7"GOOD THINGS GO THROUGH SMALL FASSAGES. ° 

$l. ADDER 

You are at the base of a huge ladder reaching upt 
and out of sight. It must 

extend up at least SOO feet. It will take+ 
someane very brave in heart toa 

scale it. There are also passages here whicht 
lead to the north and down. 

SMAZE 

You are in a maze of featureless passages. 
$STAIRS 

You are on a steep stairway of rough-hewn+ 
granite blocks. Your lamp only 

penetrates the glaom for a few feet in either+ 
direction. Above you is a 

doorway leading to a large room. Helow is at 
dark and drafty continuation of 

the stairway. 

$ECHOES 

You are in a vast underground cavern. Thet 
slightest sound seems to create a 

resonating wave of echoes. It is difficult tot 
even hear yourself think. To 

the north an opening leads to a downward+ 
slanting corridor which quickly bends 

out of sight. In the center of the cavern,+ 
there is a set of rough stairs 

leading downward. Behind you a similar sett 

of stairs leads upward. 

SWARMROOM 

You are in a confined space which is more liket 
a widening of the corrider than 

a room. The corridor itself ends abruptly here,+ 


289 


but a narrow shaft opens 

downward. There is a faint red glow to be seent+ 
if one peers into the shaft 

and a warm draft of slightly sulfuroust+ 

smelling air emanates from that open- 

ing. The corridor slants downward to the south. 
$ INCLINE 

You are in a twisting, downward slanting corridor.+ 
The corridor forms a 

Y-shaped intersection here with passages leadingt 
up, east, and west. 

SHONE YCOMB 

You are in a room with numerous honeycomb-Llike+ 
openings leading out. The 

largest of these appears to lead south. 
SROUNDROOM 

You are in an almost spherically shaped roamt 
with exits to the west, north, 

and down. 

SMUDROOM 

You are in a room filled with thick, knee-deep mud.+ 
Trickles of water flow in 

from a narrow opening high above you on thet 

west wall. A passage leads up 

from here and another down. 

$DEEFFOOL 

You are standing by a deep underground pool.+ 

The water is ice cold, but clear 

as crystal. There is nary a ripple an the surface. 
$COLDROOM 

You are at the bottom of an icy cold underground+ 
pool. If you don’t get out 

quickly, you’ll either drown or freeze to deatht 
or both. 

SNARROWS 

You are in a slanting N-S passage. 

SNARROW4 

You are in a sloping N-S passage. A narrow slitt 
opens downward, from which 

emanates a warm, sulfurous draft. A very faint+ 
red glow may also be seen 

through the slit. 

SRIVER 

You are standing on the eastern shore of a swiftlyt+ 
running N-S underground 

river. 


290 


$ROCE YROOM 


You are in a room filled with irregularly shaped+ 


boulders. To the east a low 

opening below a huge boulder leads to ant 
alcove of some sort, and to the south 

the passage continues along the river bank. 
$SILTROOM 


You are near a large underground deposit of silt.+ 


A passage leads upward. 


From it steadily oozes a flow of thick mud which+t 


must contribute to the 

deposit. Passages also lead north and west. 
SAL COVE 

You are in a small nook off a large room+ 
filled with boulders. The only exit 

is to the east. Toa the west you can see intot 
the boulder room, but the exit 

is blocked by a large, slippery boulder.+ 

A note here says: KEEF ON DIGGING! 

$FLAMES 

Unfortunately, you have fallen into ant 
underground lava pocket. It is the 

source of the heat that produces the geysers+t 
in the steam room. You have 

been, to put it politely, “toasted to a crisp!’ 


291 


Save as “MTADV.TEXT” (per page 232) 


See Chapters 18-23, pages 145-189, 
for instructions on making the database files. 


See page 170, “CONTINUATION FILE LINES” 
if you cannot fit the entire database into a 
single text document. 


Appendix C 
The Adventure 3 Database 


fat the spectacular mountain view 

You are in mountains. To see all the snow-capped+ 
monsters hereabouts, 

you would need to look up and turn in a full+ 
circle. Near here is a 

deserted monastery. It is said that a sect of+ 
mountain worshippers once 

lived there. It is reputed that they have hiddent 
various treasures in 

these parts. It is also said that somewhere int 
these mountains there 

lives a wise seer who knows the secrets of life.+ 
Ferhaps you will be 

able to locate him. Should you succeed int 
finding him and learning his 

ways, you will receive many rewards. Watch out+ 
for treacherous passages. 

Do not necessarily believe all you see and hear. 
fwalking along a steep mountain trail 

You are walking along a steep mountain trail,.+ 
The footing is tricky 

here. Several snow-capped peaks are now visiblet+ 


292 


in front of you. 

f$walking along a narrow mountain trail 

You are walking along a narrow mountain trail.+ 
The footing is tricky 

here. Several snow-capped peaks are now visiblet 
in front of you. 

fon a steep mountain trail 

You are walking along a steep mountain trail.+ 
The footing is tricky 

here. Many snow-capped peaks are now visiblet+ 
in front of you. 

fon a narrow mountain trail 

You are walking along a narrow mountain trail.t+ 
The footing is tricky 

here. Many snow-capped peaks are now visiblet 
in front of you. 

$walking along a steep trail 

You are walking along a steep mountain trail.+ 
The footing is very 

tricky here. You see snow-capped peaks int 
front of you. 

walking along a narrow trail 

You are walking along a narrow mountain trail.+ 
The footing is very 

tricky here. You see snow-capped peaks int 
front of you. I seem to 

remember having been here before. 

scrambling through a rock-strewn col 

You are scrambling through a rock-strewn col.+ 
Above and below you are 

ledges that look safer than where you are now.+ 
Every now and then I 

hear faint echoes of churlish voices. It ist 
difficult to tell fram 

which direction they come. 

$in a tricky, rock~strewn col 

You are trying very hard not to lose yourt+ 
footing. This is a very 


tricky, rock~strewn col. Above and below yout : 
are secure looking “fairly” 


ledges. They are quite a distance in either+ 
direction, but I think 

you could make it to either one safely if yout 
are careful. 

in a col with ledges above and below 

You are scrambling through a rock-strewn col.+ 


293 


Abave and below you are 

ledges that look safer than where you are now.+ 
Every now and then I 

hear faint echoes of churlish voices. It ist 
difficult to tell which 

direction they come from. 

$in a rocky col 

You are in a rocky col. It is difficult tot 
see anything from here as 

yau cling to the mountain. The air is cold+ 
and the wind whistles as 

it blows across the bare rock~scape. 

$in a rubble filled hollow 

This is a rubble filled hollow. Talus from thet 
higher points in the 

surrounding terrain has accumulated here over+ 
the eons. It is quite 

difficult to maintain your footing in places. 
$in a hollow filled with rubble 

fat a grassy mound 

Yau have surmounted a grassy maund. There ist 
a pleasant meadow here 

filled with colorful wildflowers. Thet+ 
fragrance is stimulating. I 

think I see an ancient looking building in thet 
distance. It is 

partially hidden from view. 

fat an ancient monastery 

You have found the ancient monastery. Thet+ 
building is partially 

collapsed and all the entrances have beent 
blocked with undergrowth or 

boarded up with great hand~-hewn planks of what+ 
looks like very hard 

wood. The tatters of what must once Waa been+ 
a flag flutter in the 

light breeze from a high and pointed parapet.+ 
There is almost total 

Silence here apart from the sighing of the wind+ 
and the noise of birds 

and small animals. Yet there seems to be at 
ghostly echo of gongs, 

cymbals, plaintive and monotonous chanting, and+ 
a rushing noise like 

fire consuming an enormous pile of dead branches+ 


294 


and leaves. It leaves 

an impressian in the mind alone ~- the ears heart 
nane of it. 

The building leans into the sides of the steept 
mountain path. Leading 

to the now blocked front entrance is a set of+ 
broad wooden stairs. Set 

inte the bottom of this staircase is at 
windowless wooden gate. Tt is 

hanging askew from its rotten hinges ~ I think+ 
you could squeeze by it 

if you wished ta. 

$in a dank and musty cellar 

You are in a dank and musty cellar. It looks+ 
like it might have once 

been a storeroom for the monastery. There aret 
empty barrels standing alang 

the back wall. There are rows of dusty shelves, + 
mostly empty save for an 

accumulation af cobwebs. In the floor are twot 
trapdoors or hatches made 

of the same rough-hewn planks seen outside.+t 
They both shut tightly. 

Sin an ancient catacomb 

This 15 an ancient catacomb directly under thet 
monastery. The light is 

extremely dim caming only from the cellar ahovet 
down the steep stone 

staircase. The floor is littered with bones.+ 
Bits of what appears to 

be dried flesh still cling to them in many cases. 
fon a steep mountain slope 

You are on a steep mountain slope. There is not 
vegetation here, you 

have passed beyond the treeline. The baret 
granite peeks out here and 

there from beneath the fresh dusting of snoaw.+ 
There are massive drifts 

to either side. Tread lightly, avalanches aret 
easily caused here by 

sudden noises. 

fan a slope beyond the treeline 

You are on a steep mountain slope. There is not 
vegetation here, as you 

have passed beyond the treeline. You see bare-+ 
granite here and 


295 


there fram beneath the fresh dusting of snow.+ 
There are massive drifts 

on either side. Tread lightly, avalanches aret 
easily caused here by 

sudden noises. 

fon a slope surrounded by drifts 

You are on a steep mountain slope. There ist 
not any vegetation here. 

You are past the treeline. Kare granite peekst+ 
fram beneath the fresh 

SNOW. There are also massive drifts to eithert 
side of the marrow trail. 

I would be extremely careful about loud+ 

noises ~ avalanches have been 

known to be caused by incautious travelers. 

fon a slope bereft of vegetation 

You are on a steep mauntain slope. There ist 
not any vegetation here. 

You are past the treeline. Hare granite peekst+ 
from beneath the fresh 

SNOW. There are also massive drifts to either+ 
side af the narrow trail. 

I would be extremely careful about loud+ 

noises ~- avalanches have been 

known to be caused by incautious travelers. 

fon a slope with granite underfoot 

You are on a steep mountain slope. There ist 
not any vegetation here. 

You are past the treeline. Bare granite peekst 
from beneath the fresh 

SNOW. There are also massive drifts to either+ 
side of the narrow trail. 

I would be extremely careful about loud+ 

noises - avalanches have been 

known to be caused by incautious travelers. 

$in a deep ravine 

You are in a deep ravine. All you can see ist 
the tattered clouds 

above. They are racing along beneath the bluet+ 
sky, carried by the 

calamitous mountain gales. 

$in a deep crevass 

You are in a deep crevass. There is a ridqget 
above you ~- I think you 

might be able to scramble back up with some effort. 
fin a bitterly cold deep ravine 


296 


You are in a deep ravine. Al1I you can see ist 
the tattered clouds 

above. They are racing along beneath the bluet 
sky, carried by the 

calamitous mountain gales. Although the sunt 
appears now and then 

it is bitterly cold here. 

fon a steep pitch 

You are on a very steep nitch. The bare rock+ 
beneath your feet is 

treacherous in the extreme. Any false movest 
here and you will not live 

to tell your grandchildren about them. 

fon an extremely steep pitch 


fon a very steep pitch 


fon a steep pitch with bare rock 


$on a pitch with bare rock 

it) 

fan a mountain peak 

You have reached a magnificent mountain peak.+ 
All around you lies the 

grandeur of the mountain range. You can seer 
several other peaks. They 

look so near you could almost reach out and+ 
touch them, yet they must 

be several miles away, even as the crow flies.+ 
The wind here howls in 

your ears, but does not diminish your pleasuret 
in the vista. 

fon a splendid peak 


fon a magnificent peak 
fon a grand mountain peak 
fon a peak 


atop a saddle like formation 

You are atop a saddle like formation.+ 

In some directions there are 

dropoffs and in others the trail continues upward. 
fon a saddle 


297 


fatop a saddle 


$in a mountain cave 

This is a mountain cave. It continues on intot 
the heart of the 

mountain. The passageway is very narrow and+ 
filled with debris. 

There are pits in the floor of the cave.+ 

Some coantain the remnants 

of fires: charred wood and ashes. Some of thet 
wood is very strange 

looking ~ sart of whiteish and quite lang and narrow. 
$deep in a cave 

You are in a cave in the very roat of thet 
mountain. It is pitch 

black in here. I wouldn’*t stay long ~ there are+ 
nameless things which 

see in the dark. 

Sin a dimly lit mountain cave 

This is a mountain cave. The light here is dim,+ 
but further along 

the passage there appears to be an opening from+t 
which it issues. 

There is a draft coming from the darker side of+ 
the passageway. It 

whistles along and now and then seems to whisper.+ 
You can almost 

make out faint words, but they make you shudder+ 
and want to turn 

away . 

$in a cave 

This is a mountain cave. The light here is dim,+t 
but further along 

the passage there appears to be an opening from+ 
which it issues. 

You feel a slight draft on your face. 

in a cave in pitch dark 

You are in a cave in the very root of a mountain.+ 
It is intensely 

dark here ~- you cannot see a hand held inches fromt 
your face. AS you 

move, you brush against tendrils of a featheryt 
substance. It makes 

your flesh crawl. 

$in a cave with bones strewn about 

This is a mountain cave. There are remnants oft 


298 


cooking fires here. 

There are old bones strewn on the floor.+ 

There are strange looking 

runes or symbols of some sart carved into the walls. 
$in a shallow gully 

You are in a shallow gqulily.+t+ 

It seems to deepen further ahead. 

Fin a deepening qulily 

You are in a deepening gully. In onet+ 

direction it seems shallower. 

I can’t say for sure, but in the other directiont 
there may be an 

opening into the side of the mountain. 

$on a barren hill 

You have surmounted a barren hill. There is thet 
barest hint of foliage 

here, but most has succumbed ta the rigorous+ 
climate and the altitude. 

fat a sheltered place 

This is a sheltered place in the path. It is at 
very deep cut in the 

side of the mountain, not quite a cave, but mucht 
mare than a mere 

depression. 

fon a narrow ridge 

This is a narrow ridge. The trail is only'’a foott+ 
or so wide here and 

negotiating it will be quite a challenge.+ 

There are precipitous 

dropoffs in some directions and the chasm yawns+ 
beneath you. 

on a very narrow ridge 

This is a narrow ridge. The trail is only a foot+ 
or so wide here and 

negotiating it will be quite a challenge.+ 

There are precipitous 

dropoffs in some directions and the chasm yawns+t 
beneath you. 

fat the grandest peak 

You have reached the grandest peak in the entiret 
mountain range. Here 

there is almost an entire acre of flat from whicht 
to view the 

surrounding peaks. All of the other pinnacles+ 
are spread out beneath 

the one you have reached. 


299 


On the far side of the small flat of the peak ist 
a wizened ald man. 

He appears to have been here quite same time.+ 
Perhaps he can tell you 

some interesting tales. 

$chasm 

You have fallen off the mountain side.+ 

The chasm that yawned beneath 

you has claimed you for its own. The drop of+ 
several thousand feet may 

not have been enough ta finish you off, but thet 
sudden stop at the 

bottom certainly was. 

Snowhere 

$cellardawn 

fat fountain of youth 

You have located the fountain of youth.+ 
Never a wrinkle shall grace your 

countenance. Your quest is culminated.+ 
Cangratulations. 

Strtalk 

There are several huge, ugly mountain trollst 
lolling nearby. 

Unfortunately, they have spotted you.+ 
Quickly and rudely you are 

surrounded and bound with rough thangs of+ 
ragged and grisly leather. No 

doubt the remains of the trolls last and+ 
(for them) meagre meal. Now 

they have yau!! 

With great gusta they truss you up to at 
gnarled and charred hardwood spit. 

This, of course, is accompanied by flayingt 
you and using the strips of your 

flesh to make your bonds even more secure. + 
They then slowly roast you to 

a turn over their hastily built fire.+ 

You make a fine meal for these 

famished, gluttonous villains. 

The last sounds heard by your erstwhile, + 

but discretely hidden, guide are 

those of vast troll cantentment. Snores,+ 
great rude belches, sSlavering over 

the last remnants of roasted flesh an what+ 
were your bones, and other more 

unspeakable sounds. I beat a hastyt 


300 


retreat from this gruesome location and 

bid you better luck in your next+ 
re-incarnation. 

Swmmb 1 ab 

The wise man of the mountain speaks: 

*T am exceedingly pleased at your obeisancet+ 
and your gifts. In return 

for your devotion, I offer you a word.+ 

That word is: wadda. Use it well, 

my san.” 

Swmmharp 

The wise man of the mountain lifts a deept 
hood covering his face. He gazes 

at the items arrayed before him and speaks: 
"You give me great displeasure oh profligatet 
servant. Of silver and gold 

I will have none. Treasures of the earth+t 
are but transitory baubles to be 

scorned and discarded. Take them from my+ 
sight and bring me gifts of worth 

to the spirit. I would sooner have one molart 
from the jaw of a troll than all 

these worldly goods. If you seek my wisdom, + 
you will heed this charge.’ 

S$wmmhella 

The garbed figure cames toward you.+ 

He speaks: 

"Welcome, my child. You have achieved much+ 
simply to reach my mountain peak. 

If you bring to me the treasures that I seek,+ 
then you shall know the secret 

of everlasting youth! Ga now and fulfill+ 
your quest. * 

fwmmsp 1 

The wise man speaks: 

"So you return, my child. I have beent 
exceedingly occupied by my efforts at 
omphaloskepsis. You will excuse me if I+ 

do not linger.’ 

fSwmmsp2 

The wise man speaks: 

"A seer once told me that only those whot 
persist succeed in finding the true 

secrets of existence. Do net despair, yourt 
quest has just begun. You have 

much to learn and much to gain. Io must+ 


301 


7 


now feed and tend my yak. 
fwmmsp > 

The wise man speaks: 
"Have you read any goad scrollst 

lately my child?’ 

Swmmsp 4 

The wise man speaks: 

*Cantemplation is good for the soul, myt 
child. But music and prayer also heal 
many wounds. * 

Swmmsps 

The wise man speaks: 

"All that is gold does not glitter,+ 

my child. A little wine for the stomach’s 
sake. A stitch in time saves nine.t 

Don*t take any wooden nickels. A flush 
beats two pair any day, but then I don’*t+ 
have indoor plumbing up here. Am JI 

boring you? Maybe I should go say my prayers.’ 


$tgablure 

The trolls seem very excited. I can*’t+ 
tell what they want more ~- you or 

those bones you are carrying. 
$tqabbones 

The trolls seem to have lost interest+ 
in you ~- at least for the moment. 


They are gathered around the bones+ 
you have dropped. 

Wait... They are attacking the bones+ 
with great gusto. There are crunching 
noises and sounds of breaking teeth.+ 
I would take this opportunity to make 
tracks. 


$trhungry 
Several huge, ugly, mountain trolls aret 
lolling nearby. Unfortunately, 


they have spotted you. They slaver andt 
utter gutteral blasphemies. They 

gaze longingly at the bones you aret 
carrying. They rise. They seem to 

want to follow you. I would find a qood,+ 
out of the way place to ditch 

those bones if I were you! 

$trfollow 

You are being shadowed by several huge,+ 
hairy, rude, and hungry mountain 


302 


tralls. They slaver and mumble in their+ 
gutteral speech. You can barely 
understand, but the general idea is that+ 
they are famished and want bones 

and flesh. Perhaps it is yours+ 

that they will get! 


Write your own “helpspiel” and add it to the end of the description database: 


Shelpspiel 

The Abominable Snowman arrives with a tray of snow cones. He made them 
himself! After a delicious treat and a hearty conversation about the many types 

of snowflakes, he apologetically explains that he knows only as much as you about 
the Wise Man of the Mountains and the Fountain of Youth, and he soon departs. 


303 


Index 


Index 


A 
Adams, Scott, 1 
Adventure, 1 
Adventure code, 191 
Adventure databases, structure of, 
177 
Adventure language, 191 
Adventure on disk, 191 
Adventure 1, 15 
Adventure 1, data declarations for, 18 
Adventure 1, functions and proce- 
dures, 18 
Adventure 1, listing for, 23-53 
Adventure 1, program code outline for, 
17 
Adventure 1, structure diagram, 21 
Adventure 1, the map of, 55 
Adventure 1, the maze map of, 56 
Adventure 3, 192 
Adventure 3, code outline of, 193-195 
Adventure 3, database for, 250, 292 
Adventure 3, extending, 254 
Adventure 3, listing for, 203 
Adventure 3, map of 197-201 
Adventure 3, problems in, 245 
Adventure 3, problems of, 201 
Adventure 3, program outline of, 193 
Adventure 3, relation of units for, 196 
Adventure 3, summary map of, 196 
Adventure 3, units in, 239 


Adventure 2, 68 
Adventure 2, database for, 286 
Adventure 2, data declarations, 70 
Adventure 2, listing for 78-114 
Adventure 2, main program block, 73 
Adventure 2, map for, 75, 76 
Adventure 2, problems in, 130 
Adventure 2, problems of, 77 
Adventure 2, procedures and function, 
71, 72 
Adventure 2, program outline, 69 
Adventure 2, structure diagram, 74 
Adventures, systematic approach to 
writing, 256 
Adventures, writer's checklist for, 256 
AND, 131 


Apple ll, 12 

B 
Backup disks, 143 
Beings, 1, 4, 6 


Block structured, 63 
Boolean expressions, 131 
Boolean operators, 131 
Boolean variable, 130 
Browse, 145 

Browse program, 175, 181 


c 
Case statements, 59 


Clues, 8 

Cmdlookup function, 116, 118 
Code file, 241 

Command dispatcher, 116 
Command handling code, 116 
Command processing, 115 
Commands, 115 

Continuation file instruction lines, 170 
Controlling the game, 60, 115 
Conversion of representations, 120 
Conversion of values, 119 
Crowther, Willie, viii 


D 
Database, 137, 177, 250, 286, 292 
Database concepts, 145 
Database index, 145 
Database of descriptions, creating a, 
166 
Declarations, local, 63 
Description lines, 168 
Descriptions, 190 
Descriptions index, 177 
Dig command, 138 
Direction, deciding on a, 61 
Ditto lines, 169 
Docommand procedure, 116, 118 
Dungeons and Dragons, ix 


E 
Eat command, 138 


307 


Editor limitations, 139 Makedese, running, 171 s 


Embeliising the map, 6 Make80 database generator listing, Scanners, 116 
Enumerated type, 54 147 Scoring, 137 
Enumerated types, 118, 131 Map, 254 Search, linear, 121 
Escape key, 143 Map layout, 6 Searching, 121, 122, 183 
Events, 130 Map organization, 6 Semantics, 240 
Exploration, 3 Maps, 192 Sentinels, 118, 123 
Menu-driven, 13 Set, 125 
F Microsoft, Inc. vii Set relationships, 131 
File creating program, 174 Monsters, 1, 4, 6 Set variables, 127 
File managing, 139 Sierra On-Line, ix 
File reading program, 174 N Skeleton, 66 
File variables, 137 F Skeleton adventure, 256 
Files, loss of, 142-143 bath rican ae Skeleton adventure, listing for, 261 
Files, random access, 173 Nocmd, 123 , Source file, 139, 140 
Files, sequential, 173 NOT, 131 Special conditions, 2 
‘ String comparison, 122 
H Structured design, 16 
Hash function, 184 0 Surprise, 10 
Hashing, 182-183 Objects, 1, 3, 6 Swapping mode, 141 
Hash table, 185 Objlookup, 128 Symbol tables, 182 
Hash value, 183-184 Ogre, 138 Syntax, 240 
Head string, 117, 120 Operator procedure, 132 
Help command, 137 OR, 131 T 
Tail string, 117, 120 
I P Template, 66 
Index files, 177 Travel, 136 
Infocom, vii eras 18 Travel, simplification of, 136 
arsers, 116 T Ryley 
Pascal. ix. 12 ravel indicators, 5 
K Pear : 127 Travel procedure, 116 
K(runch command, 141 Bary pies dure, 128 Treasures, 1, 3 
Pdrop, 127 Turns, counting of, 135 


Pflames procedure, 67 


Pisland procedure, 66 U 


Unit, implementation part of, 241 
Linear search, 121 ees 67 Unit, interface part of, 241 
Linear searches, 183 P : Units, programming in, 239 
Linked lists, 183 ee as USCD, ix, 13 
Linking, 198 ate ih jaa USCD limitations, 252 
Linking programs, 242 Poi : Uses statement, 242 

F pit procedure, 67 
Listen procedure, 116, 118 Preconditions, 132 
Local procedures, 63-64 Prefix #5. 1 At v 
Location, the adventurer’s, 60 Problem diffi culty, 9 
Locations, 1, 2,5 Ys 


; ; Problems, 2, 4, 6, 130 
Locations, changing, 60-61 Pstart procedure, 67 


Lamp command, 137 
Light command, 137 


Values, external, 119 
Values, internal, 119 
Variables, file, 137 


Logic, 9 
Logical connectives, 131 Pvestibule procedure, 67 Ww 
Lookup techniques, 183 Whichway function, 116 
R Woods, Don, viii 

M Random access, 175 Writer's checklist, 256 
Main program, 240 Relational operators, 131 
Main program block, 17 Repeatability, 9 v4 
Makedesc, 145, 167 Rooms, 1, 2, 5 ZORK, 1 


308 Edited by Marilyn L. Johnson 


Programming Your Own 
Adventure Games in Pascal 


If you are intrigued with the possibilities of the program included in Programming Your Own 
Adventure Games in Pascal (TAB Book No. 1768), you should definitely consider having the 
ready-to-run tape containing the software applications. This software is guaranteed free of man- 
ufacturer’s defects. (If you have any problems, return the disk within 30 days, and we'll send you a 
new one.) Not only will you save the time and effort of typing the programs, the disk eliminates the 
possibility of errors that can prevent the programs from functioning. Interested? 


Available on disk for Apple II, Il+ with 48K, Apple language card or other 16K RAM card, and Apple 
Pascal system; Apple //e with Apple Pascal system at $19.95 for each tape or disk plus $1.00 each 
shipping and handling. 


ee er ar mS ee Pe ng ee tg eT ge ee eT ee 


I’m interested. Send me: 


disk for Apple Il, Il+, He (requires Apple Pascal system) (6229S) 
TAB BOOKS catalog 

Check/Money Order enclosed for $19.95 

plus $1.00 shipping and handling for each tape or disk ordered. 


VISA _______ MasterCard 

Account No, ———_____—CO—C_.séEEpires 
Name 
Address 
City) se ee SANS: ee ZIP 
Signature 
Mail to: TAB BOOKS Inc. 

P.O. Box 40 


Blue Ridge Summit, PA 17214 


(Pa. add 6% sales tax. Orders outside U.S. must be prepaid with international money orders in U.S. dollars.) 


TAB 1768 
| ee 


ie Sp ea Neste See a SE ah SS ea ee SE eS ee ee ea ee 


PROGRAMMING YOUR OWN 
ADVENTURE GAMES IN 


PASCAL 


BY RICHARD C. VILE, JR. 


Imagine! 

You can write 
your own exciting 
adventure games... 
just like a pro! 


TAB BOOKS inc. 


Blue Ridge Summit, Pa. 17214 


Send for FREE TAB Catalog describing over 750 current titles in print. 


ISBN O-&306-0768-4 


tM ‘ 


ammi 79 oun own adven 


sed: Good 
BUKAGaseseoe 


| 


TVWOSVd NI SAWVS AYNLNAAGV 
NMO YNOA ONINWVYDOUd ~ bi: 


ATA